Simulating Turbo Codes using a Modular Simulation Platform by David C. Lee Submitted to the Department of Electrical Engineering and Computer Science in Partial Fulfillment of the Requirements for the Degree of Master of Engineering in Electrical Engineering and Computer Science aitKEN at the MASSACHUSETTS INSTITUTE OF TECHNOLOGY MASSACHUSETS WSTITUTE OF TECHNOLOGY May 24, 2002, JUL 3 1 200? Copyright 2002 David C. Lee. All rights reserved. LIBRARIES The author hereby grants to M.I.T. permission to reproduce and distribute publicly paper and electronic copies of this thesis and to grant others the right to do so. Author Department of Electrical Engineering and Computer Science May 24, 2002 Certified by Nick Zogakis VI-A Company Thesis Supervisor Certified by_ Vahid Tarokh M.I.T. Thesis Supervisor Accepted by_ Arthur C. Smith Chairman, Department Committee on Graduate Theses -4 Simulating Turbo Codes using a Modular Simulation Platform by David C. Lee Submitted to the Department of Electrical Engineering and Computer Science May 24, 2002 In Partial Fulfillment of the Requirements for the Degree of Master of Engineering in Electrical Engineering and Computer Science ABSTRACT A simulation tool was created to evaluate the performance of Turbo codes proposed for digital subscriber loop channels. The tool is modular and was designed to be highly flexible. In particular, the use of linked list data structures to handle module input and output data flow allows the tool to be structurally flexible. Module definitions are defined comprehensively and allow for high component flexibility, as well. Expressions are provided for MAP decoding of convolutional codes under 4-QAM and under higher spectral efficiency modulations. Simulation results match cited results relatively well and indicate that Turbo code algorithms are implemented correctly in the tool. Results offer confidence in the ability of the simulation tool to correctly estimate the performance of Turbo codes. Thesis advisor: Vahid Tarokh Title: Associate Professor VI-A company supervisor: Nick Zogakis Title: Systems Engineer -3- -4- ACKNOWLEDGMENTS The majority of work for this thesis was completed on-site at Texas Instruments, Incorporated, under the mentorship of my supervisor Nick Zogakis. His suggestions and reviews were invaluable throughout the course of this study. The other members of my group at Texas Instruments were of great help in their generous contribution of time and resources. In particular, my many insightful discussions with Chia-Ning Peng and Elisa Pasquali helped to clarify several issues. During the simulation intensive phase of this study, Texas Instruments contributed valuable computing time through the use of its workstations. In particular, Nick Zogakis, Konrad Kratochwil, and Brian Weise, were generous in allowing me to use their workstations for the simulations. I would also like to thank Krista Jacobsen and Patrick Wang for their welcome support and encouragement throughout this study. At MIT, my thesis advisor Vahid Tarokh was very enlightening in his responses to my technical questions. Further, his comments and suggestions about the content of this thesis greatly helped to shape it into its final form. I thank Professor Marcus Zahn and Ms. Lydia Wereminski of the MIT VI-A program, and my VI-A advisor Judy Hoyt, for my very positive experiences under the program. Lastly, I thank both Texas Instruments and MIT for their support of this study. It has been a rewarding experience! -5- -6- CONTENTS I Introduction 15 2 Background Transm ission using Signal Sets ........................................................... 2.1 T he Shannon L im it .............................................................................. 2.2 Probability of Error .............................................................................. 2.3 2.4 Calculating BER for an AWGN Channel ............................................. C hannel C oding .................................................................................... .2.5 2.6 The C onvolutional C ode ....................................................................... Recursive Systematic Convolutional Code ........................... 2.6.1 Markov Structure of Convolutional Codes ............................ 2.6.2 Decoding using Maximum a posterioriProbability .............. 2.6.3 2.7 Symbol Mapping and Channel Observations ...................................... 4-Q A M M odulation ............................................................... 2.7.1 2.7.2 Higher Spectral Efficiency Modulation ................................. 2.8 T he T urbo C ode .................................................................................... The Turbo Encoder ................................................................ 2.8.1 2.8.2 Iterative Decoding and the Turbo Decoder ........................... 2.8.3 Fast Decoding for Rate-1/2 Component Encoders ................ 19 19 20 22 23 24 25 26 27 28 31 32 33 36 37 38 41 3 45 The Simulation Tool 45 3.1 Simulation Tool Scope ........................................................................ 47 .................................. Flexibility for Structural Design Considerations 3.2 47 3.2.1 Simulation Tool Language .................................................... 48 3.2.2 Module Flow Implementation ............................................... 49 3.2.3 Data Transfer between Modules ........................................... 3.2.4 M odule Execution .................................................................. 50 51 3.3 Design Considerations for Component Flexibility ............................... 52 3.3.1 Linked List Design and Implementation ............................... 53 Interleaver Design and Implementation ............................... 3.3.2 55 3.3.3 Convolutional Encoder Design and Implementation ............. ............... 55 for Encoding Use of the Trellis Diagram 3.3.3.1 57 3.3.3.2 Generating Polynomials ........................................ 58 3.3.3.3 Constructing the Trellis Diagram ........................... Puncturing / Un-puncturing Module Design and Implementation 3.3.4 60 ................................................................................................. 62 Signal Mapper Design and Implementation .......................... 3.3.5 -7- 3.4 3.3.6 Channel and Demodulator Design and Implementation ........ 3.3.7 MAP Decoder Module Design and Implementation ............. Sim ulation C ode O rganization ............................................................. 64 65 66 4 Simulation Results 4.1 Results for Double Parallel Concatenated Turbo Code 4.1.1 Sim ulation 1: Rate-1/2 Turbo code ....................................... 4.1.2 Simulation 2: Rate-1/2 Turbo code ....................................... 4.1.3 Sim ulation 3: Rate-2/4 Turbo code ....................................... 4.1.4 Simulation 4: Rate-2/4 Turbo code ....................................... 4.1.5 Simulation 5: Rate-2/4 Turbo code ....................................... 4.1.6 Sim ulation 6: Rate-3/4 Turbo code ....................................... 4.1.7 Simulation 7: Rate-3/4 Turbo code ....................................... 4.1.8 Simulation 8: Rate-3/6 Turbo code ....................................... 4.1.9 Sim ulation 9: Rate-4/6 Turbo code ....................................... 4.1.10 Sim ulation 10: Rate-4/6 Turbo code ..................................... 4.2 Results for Triple Parallel Concatenated Turbo Code .......................... 4.2.1 Sim ulation 11: Rate-1/2 Turbo code ..................................... 4.2.2 Simulation 12: Rate-1/2 Turbo code ..................................... 4.3 Sum m ary of Results .............................................................................. 69 70 71 73 75 76 78 80 81 83 85 87 89 89 91 92 5 Discussion 5.1 Reliability of Simulation Tool .............................................................. 5.2 Unexpected Simulation Results ............................................................ 5.3 Sim ulation Speed and Mem ory Usage ................................................. 95 95 96 97 6 Conclusions 99 A Main Simulation Code turbosim -std.c ....................................................................................... turbosim -icoding.c .............................................................................. turbosim -uncoded.c ............................................................................ 101 101 108 114 B Sample Output for turbosim-std.c Screen Output ....................................................................................... 119 119 123 Output File test.txt C ....................................... Simulation Code general.h ................................................................................................ general.c ................................................................................................ linkedlist.h ............................................................................................ linkedlist.c ............................................................................................. source.h ................................................................................................. source.c ................................................................................................. interleave.h ........................................................................................... interleave.c ............................................................................................ -8- 125 125 126 128 129 135 136 139 140 co n v .h ................................................................................................... co n v .c .................................................................................................... puncture.h ............................................................................................. puncturex ............................................................................................. mapper.h ............................................................................................... m apperx ............................................................................................... channel.h ............................................................................................... ch an nel.c ............................................................................................... demod.h ................................................................................................ d em o d .c ................................................................................................. decoder.h ............................................................................................... d eco d er.c ............................................................................................... 14 8 14 9 159 160 165 166 177 17 8 180 18 1 184 18 5 197 References -9- -10- LIST OF FIGURES 2-1 2-2 2-3 2-4 2-5 2-6 2-7 BER curve for uncoded 4-QAM ................................................................ Rate-1/2 recursive, systematic convolutional code .................................... Rate-2/3 recursive, systematic convolutional code .................................... Markov diagram of a four state, rate-1/2 convolutional code .................... Trellis diagram ............................................................................................ C hannel coding .......................................................................................... Signal points and labels for 4-QAM ........................................................... 25 26 26 27 28 29 32 2-8 2-9 Signal points and labels for 16-QAM ......................................................... Turbo encoder structure .............................................................................. 34 37 2-10 Turbo decoder structure .............................................................................. 40 3-1 3-2 3-3 3 -4 Turbo encoder from RN-027 .................................................................... Turbo encoder from CF-072 ....................................................................... Turbo encoder from RN-079 .................................................................... L inked list .................................................................................................. 46 46 46 50 4-1 4-2 4-3 4-4 4-5 4-6 4-7 4-8 4-9 4-10 4-11 4-12 4-13 4-Q A M signal set ........................................................................................ 16-Q A M signal set ...................................................................................... Simulation 1 BER curves for Table 4.2 and Table 4.3 ............................... Simulation 2 BER curves for Table 4.2 and Table 4.4 ............................... Simulation 3 BER curves for Table 4.6 and Table 4.7 ............................... Simulation 4 BER curves for Table 4.6 and Table 4.8 ............................... Simulation 5 BER curves for Table 4.6 and Table 4.9 ............................... Simulation 6 BER curves for Table 4.11 and Table 4.12 .......................... Simulation 8 BER curves for Table 4.16 and Table 4.17 ........................... Simulation 9 BER curves for Table 4.19 and Table 4.20 ........................... Simulation 10 BER curves for Table 4.19 and Table 4.22 ......................... Simulation 11 BER curves for Table 4.24 .................................................. Simulation 12 BER curves for Table 4.25 .................................................. 70 70 73 74 76 77 79 81 84 86 88 90 92 - II - - 12- LIST OF TABLES 3.1 Sum m ary of sim ulation files ............................................................................. 68 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 4.13 4.14 4.15 4.16 4.17 4.18 4.19 4.20 4.21 4.22 4.23 4.24 4.25 4.26 Simulation I puncturing and mapping scheme ................................................. Approximate BER values from cited graphs for simulation I Turbo code ....... Simulated BER values for simulation 1 after 10,000 and 100,000 blocks ........ Simulated BER values for simulation 2 after 10,000 and 100,000 blocks ........ Simulation 3 puncturing and mapping scheme ................................................. Approximate BER values from cited graphs for simulation 3 Turbo code ....... Simulated BER values for simulation 3 after 10,000 and 100,000 blocks ........ Simulated BER values for simulation 4 after 2,500 and 25,000 blocks ............ Simulated BER values for simulation 5 after 10,000 and 100,000 blocks ........ Simulation 6 puncturing and mapping scheme ................................................. Approximate BER values from cited graphs for simulation 6 Turbo code ....... Simulated BER values for simulation 6 after 1,666 AND 16,666 blocks ......... Simulation 7 puncturing and mapping scheme ................................................. Simulated BER values for simulation 7 after 1,666 AND 16,666 blocks ......... Simulation 8 puncturing and mapping scheme ................................................. Approximate BER values from cited graphs for simulation 8 Turbo code ....... Simulated BER values for simulation 8 after 1,666 blocks ............................... Simulation 9 puncturing and mapping scheme ................................................. Approximate BER values from cited graphs for simulation 9 Turbo code ....... Simulated BER values for simulation 9 after 2,500 blocks ............................... Simulation 10 puncturing and mapping scheme ............................................... Simulated BER values for simulation 10 after 2,500 and 25,000 blocks .......... Simulation 11 puncturing and mapping scheme ............................................... Simulated BER values for simulation 11 after 650 blocks ................................ Simulated BER values for simulation 12 after 1,666 blocks ............................ Sum m ary of sim ulation results .......................................................................... 71 71 72 74 75 75 75 77 78 80 80 80 82 82 83 83 83 85 85 85 87 87 89 90 91 93 5.1 5.2 Sim ulation tool m em ory usage .......................................................................... Sim ulation tool speed ....................................................................................... 98 98 - 13 - - 14- CHAPTER ONE INTRODUCTION Almost every household in the United States uses the telephone system for voice communications and the cable system for broadcast video. Although traditional uses for the telephone and cable networks are different, developments in digital communications in the last fifty years have enabled both systems to be similarly used for data communications. The telephone system was originally intended for low-bandwidth voice communications, and its network is optimized to operate primarily in the voice band [1]. Within this band, current computer modems can achieve a data rate of 56 kilobits per second (kbit/s). Compared to a cable modem, which operates over a system designed for high-bandwidth video broadcasting, this transfer rate is meager at best. However, with the advent of digital subscriber loop (DSL) technology, data communications over the - 15 - 16 CHAPTER ONE telephone network can now operate beyond the voice band and can achieve communication speeds similar to that of the cable modem. One variant of DSL technology is the asymmetrical digital subscriber loop, or ADSL. In ADSL, the data rate from the end-user to the central office (upstream) is about one-tenth the rate from the central office to the end-user (downstream). The specification for ADSL is contained in International Telecommunications Union (ITU) standard G.992.1, also known as G.dmt. G.dmt specifies an optional channel code as a performance enhancement feature. Although the use of this code is optional, its use greatly improves data rate. Since the adoption of the channel code in G.dmt, even more powerful codes have been discovered. effort. In particular, the Turbo code has been the focus of much research The Turbo code is a class of parallel-concatenated codes discovered in 1993. Research efforts have shown that with the use of Turbo codes, achievable data rates approach very close to theoretical limits [2]. As part of the ITU's ongoing commitment to improve its standards, it has posted question 4/15 (question 4, study group 15) to the telecommunications industry to solicit enhancements to G.dmt and other DSL standards. The response is that many companies have recommended various Turbo codes as a replacement for the current channel code in G.dmt. These recommendations specify different types of Turbo codes and some cite simulation results. There is currently no sure way to compare the different recommendations because there may be differences in simulation implementations and test parameters. Thus, there is need for a Turbo code simulation platform that can easily and efficiently simulate the different Turbo codes specified in the recommendations. INTRODUCTION 17 This thesis will discuss implementation issues in simulating Turbo codes, present a modular software tool for simulating Turbo codes, and analyze the performance of the software tool and the reliability of simulated results. Of particular interest is the modular implementation of the tool, which allows the tool to be highly flexible. Further, the thesis describes in detail maximum a posteriori (MAP) probability decoding of convolutional codes under 4-QAM and under higher spectral efficiency modulations. CHAPTER TWO BACKGROUND The principles of digital communication stem from a field of research called information theory. This field originated about fifty years ago through the seminal work of Claude Shannon. Shannon theorized that information can be expressed as binary digits and that the transmission of information can be quantified. Shannon was one of earliest scientists to realize binary information, and his work held important notions that paved the way for modern communication technology. This section will review concepts fundamental to digital communication and will discuss the Turbo code. 2.1 Transmission using Signal Sets In a communication channel, information is transmitted through the use of signal carriers. Each carrier occupies a specific frequency and information is placed onto a carrier by - 19- 20 CHAPTER TWO modulating the carrier's amplitude or phase. The set of all possible modulations is called a signal set, which can be represented as points in a Cartesian space. A group of b information bits is transmitted by modulating a carrier with one of M = 2bpoints from a signal set. Each of the M points represents a distinct group of b bits and is also called a symbol. A Cartesian space representation is powerful in that it allows us to use familiar concepts such as magnitude and distance on signal sets. Let P,, i=0, 1, 2, ... , M-1, be points in a signal set. The average symbol energy E, of the signal set is M-1 2 where F,.l is the magnitude of Pi. The concept of information bits will become clear in the sections that follow, but for now suppose that of the b bits in a symbol, only q bits are categorized as information bits. The average energy per information bit is defined as E Ej -- ES .(2) 2.2 The Shannon Limit A channel model that is widely used in information theory is the ideal band-limited additive white Gaussian noise (AWGN) channel. This model reflects important characteristics of real communication channels and is relatively simple and analytic [3]. In an AWGN channel, the channel is gain insensitive, and transmitted symbols are altered only by additive noise. The ratio of signal power to noise power is called the signal-to-noise ratio, or SNR, and it is an important figure in channel coding. BACKGROUND 21 Using SNR, Shannon expressed in a formula the rate R at which information could be transmitted with low probability of error. The formula defines a quantity called channel capacity, and for a channel with bandwidth W, it is (in [bits/second]) C= W log 2 (1+SNR) . (3) Shannon theorized that it is possible to transmit information at an arbitrarily low probability of error only if R < C; low probability of error is otherwise unattainable. Thus, channel capacity acts as an upper bound for transmission rates and is also known as the Shannon limit. The Shannon limit can also be interpreted as a lower bound for SNR: R<Wlog 2 (1+ SNR) R <log2 (1+ W SNR) R SNR > 2w -1. (4) The quantity p = R /W is called spectral efficiency and has units [(bits/second)/Hertz]. The relation in (4) is usually normalized with respect to p and becomes SNR 2P -I p p The quantity SNR /p is equivalent to the ratio E. /NO, where No is the two-sided noise power spectral density (PSD) of the channel. The quantity Eb /No is widely used in channel coding and is often expressed in decibels [dB]. The Shannon limit is then Eb No [dB]>10log, 0 21 p (5) ) 22 2.3 CHAPTER TWO Probability of Error As previously mentioned, symbols transmitted in an AWGN channel are altered by additive noise. The receiver must try to determine, from the altered symbols, what was actually transmitted. A simple decision rule, called the minimum distance (MD) decision rule, chooses the point in the signal set that is closest in distance to the received point. For example, suppose that a signal set is one-dimensional so that signal set points are real numbers. Further, assume that the distance between adjacent points in the signal set is constant and equal to d. Using the MD rule, an erroneous decision will occur if a symbol is altered by noise n with magnitude greater than d /2. The probability of error can be expressed using the cumulative distribution function for the Gaussian noise random variable: Prn->!= a 2 2 dy . N (6) Expressed using the Gaussianprobabilityof errorfunction Q(x)=fj e 2 dy the error probability in (6) becomes probabilityof error= Q . (7) 2(TN The quantity d /(2JN) is directly related to E /No . The energy per information bit E is inextricably related to the distance d between adjacent points in the signal set such that BACKGROUND 23 23 BACKGROUND any increase or decrease in d will affect a similar increase or decrease in Eb . The parameter oN is directly related to No in an AWGN channel by UN2 = No /2. Although various assumptions were made to arrive at the probability of error expression in (7), the underlying principle is that probability of error depends on Eb INO. The main point, shown by this particular case and applicable to other cases, is that probability of error can be and most often is calculated as a function of E, / NO . Probability of error is defined as the probability that a bit error will occur given that all previous bits were correctly decoded. A more practical measure of error is the percentage of bits that are decoded incorrectly, called the bit error rate (BER). The BER is also calculated as a function of E, INO . 2.4 Calculating BER for an AWGN Channel As previously mentioned, the noise variance is uN2 = No /2 for an AWGN channel with two-sided noise power No. Using this expression together with (2), the noise variance can be expressed as JN 2 No( E 2_. iqs =E,2 q2 b) No . (8) Parameters E, and q are predetermined and are generally not altered,'leaving E, /No as the sole variable. Using (8) for a specific value of E, NO, white noise samples can be generated from a Gaussian process with variance 2 aN . Then, these samples can be added to transmit symbols to simulate an AWGN channel. Using the MD decision rule, the CHAPTER TWO 24 BER curve for a square QAM signal set with M=4 (b=2) is shown in Figure 2-1. This curve is called an uncoded BER curve because the shape of the curve does not come from any coding scheme. 2.5 Channel Coding The BER curve in Figure 2-1 shows that an increase in the quantity E, INO results in a decrease in the bit error rate. The noise power No is a fixed characteristic of the channel and cannot be altered. Thus, Eb /No can only be increased by making E, larger, which can be accomplished by increasing the minimum distance d between points in the signal set. However, Eb is often limited by regulations specifying maximum transmit power, so the BER for a signal set can be decreased only to a certain point. With the use of channel codes, we can achieve lower BERs than that of Figure 2-1 at the same E, /No values. The BER curve generated using a channel code is called a coded BER curve. Channel coding relies on a metric called the Hamming distance, which is a measure of the number of bits that differ between two binary numbers of the same length. Channel coding adds extra bits to a group of binary numbers, called codewords in coding jargon, to increase their minimum Hamming distance. For example, the minimum Hamming distance is I for the set of codewords U = {00, 01, 10, 11}; that is, the minimum number of bits that differ between any two codewords in the set is 1. Suppose that the 2-bit codewords in U are each mapped to 3-bit codewords as follows: 00 4 000; 01 + 011; 10 + 101; 11 + 110. Now, the minimum Hamming distance between codewords in the new set P = {000, 011, 101, 1101 is 2. By increasing the minimum BACKGROUND 25 Hamming distance of a set of codewords, the decoder can better distinguish between them. The result is that the decoder possesses a limited ability to correct errors. In the codewords of set P, only two out of three bits are part of the original codewords in U; the other bit is extra. Since this extra bit must also be transmitted, the overall data transmission rate is reduced. This reduced data rate is the cost of channel coding. 1.E+00 1.E-01 1.E-02 1.E-03 wI 1.E-04 [ ______________________ ______________________ _______________________ 1.E-05 1.E-06 1.E-07 __ 3 __ 4 4 __ 5 __ 6 7 __ 8 __ 9 10 Eb/No [dB] Figure 2-1. BER curve for uncoded 4-QAM 2.6 The Convolutional Code A convolutional code is a channel code in which each output bit is formed from a linear sum of present input bits and past input and output bits. The process of generating output bits requires memory and is the same as a discrete-time convolution operation. A convolutional code with k input bits and n output bits is denoted as a rate-k/n code, with n > k. CHAPTER TWO 26 2.6.1 The Recursive Systematic Convolutional Code One type of convolutional code is the recursive, systematic convolutional code (RSCC), which is used as a component of the Turbo code. The term systematic means that k of the n output bits are exact duplicates of the input bits; these bits are called information bits or systematic bits. Thus, only (n-k) output bits are formed by convolution; these (n-k) bits are called parity bits. The term recursive means that the output bits are fed back so that each parity bit is also a linear sum of previous output bits. Figure 2-2 below shows one implementation of a rate-1/n RSCC encoder, and Figure 2-3 shows a rate-k/(k+1) RSCC encoder. In the diagrams below, S1 denotes a memory register, u1 denotes an input bit, and p1 denotes an output bit. P1 U0 3~S Figure 2-2. Rate-1/2 recursive, systematic convolutional code p1 U0 ~111 )( 30 Figure 2-3. Rate-2/3 recursive, systematic convolutional code P BACKGROUND 27 2.6.2 Markov Structure of Convolutional Codes A convolutional code can be described using a Markov model in which each state corresponds to the memory register values of the encoder and each branch corresponds to a distinct group of input bits. If there are v memory registers and k input bits, the Markov model will have 2 branches exiting each state and 2' states. The convolutional codes in Figures 2-2 and 2-3 both have 24 = 16 states, but where the code in Figure 2-2 has two branches exiting and two branches entering each state, the one in Figure 2-3 has four. The Markov diagram for a four state, rate-l/2 code might be: u0o= 1 0 U0 Figure 2-4. Markov diagram of a four state, rate-1/2 convolutional code Depending on how memory register bits are assigned, the bit So can correspond to either the most-significant bit (MSB) or least-significant bit (LSB) of the state. The example in Figure 2-4 does not make a distinction. 28 CHAPTER TWO A convolutional code is typically initialized to the zero state. The input bits at each time period determine which branch is taken and thus, the next state of the code. When the Markov diagram in Figure 2-4 is laid out for each time period, the resulting structure is called a trellis. The trellis for the Markov diagram in Figure 2-4 is shown below. When a convolutional code also ends in the zero state, its trellis is said to be terminated. 00 00 00 10/ 10 11 11 Figure 2-5. Trellis diagram 2.6.3 Decoding using Maximum a posteriori Probability The output bits of a convolutional code are mapped to a signal set point, and the point is transmitted across an AWGN channel. The output of the channel is called an observation and is also a point in Cartesian space. R =(r, r,). In two-dimensional space, the observation is The task of the decoder is to determine, from the observation, which information bits were most probably at the input of the convolutional encoder. One way to decode the convolutional code is to calculate a posterioriprobabilities (APP) and to choose the largest one. For a rate-k/n convolutional code, the data bits BACKGROUND 29 U, = i (t corresponding to a discrete time index) can have values i = 0, 1, 2, ... , 2k_- The decoder calculates the 2 a posteriori probabilities Pr(u, = i observations), and the value of i corresponding to the maximum APP is used as the decoded data bits. Thus, the decision rule is called maximum a posteriori(MAP) probability decision rule. signJ set Ut MSCC( t * AtJJGN channel obsenrdion U M decoder : R= (r., ry) Figure 2-6. Channel coding MAP decoding of the convolutional code is performed using observations over several time indices starting at index t =1 to an ending index t = 'r. Therefore, information bits are decoded in blocks. For a rate-k/n code, the information bits u, at time t correspond to a trellis branch from a state S,_, = m' to another state St = I, producing parity bits pt. Suppose there are N observation points for each block of information bits, and the vector of those points is denoted by R7N = (R-, RN ). The APP is expressed as: Pr( u, = i RN )= K t~~~~ Za,_(m'y(R,,mn', m),(in) ni )i(RI 13 M . (9) 30 30 CHAPTER TWO CHAPTER TWO ) K,,,),, is a factor used to normalize the sum of Pr(u, = i RIN across all i to unity [12]. The a and # terms in (9) are called state parameters and are defined as a, (m) = Pr(S, = in R[) (10) Pr(R, 1 St = m) .8t(in)= Pr(R, 1 R[ N+ (11) They are recursively calculated using a, (in) = Ka a,_ (m')y (R,, in', m) t+1 ,(M')=- Kg (12) m)8+nm yj(R,, 17', m#4() (13) K, and K8 are normalization parameters used to ensure that the sum of a, (in) across all m and of /, (in') across all in' is unity. The a recursion in equation (12) is initialized by realizing that if a convolutional code starts in state 0, then by the definition in (10) a(0) = 1 and ao(m)= 0 for in # 0. For the 8 recursion in (13), if a code is terminated, then we can initialize f, (0) = 1 and P, (in) = 0 for in # 0. However, if the code is not terminated, then there are two ways to initialize 8#(m) equiprobable by setting each term to 2 -, #(m). The first way is to make all where v is the number of memory registers in the code. The second way [4] is to first calculate the a recursion in (12) and then equate #,(in)= a,(in) for each m. The y term in (9) is called a branch parameteris defined as y,(R,,m',in)= Pr(R,|u, = i, S,, = in', S, = in)Pr(u, = i|S,1 = m', S, = M)Pr(S, = mIS,_, = n'). (14) BACKGROUND 31 The first probability on the right side of (14) is discussed in Section 2.7. The second term on the right side of (14) is either 0 or 1, depending on whether information bits u, =i correspond to the branch transition from St, =' to S, =m. The last term in equation (14) is the a prioriprobability, which can also be written as Pr(u, =i) for all values of i. The a prioriprobability is discussed in Section 2.8. Note that the subscript for the observation point R is somewhat ambiguous. There are r time indices and N observations per block, and the two may not be equal. In (9), the observation R, simply refers to the observation, out of the N in a block, which contains information for time index t. Thus, a specific observation may be used over multiple time indices ( N > r) or multiple observations may be required for each time index ( N < r). In general, r is an integer multiple of N. 2.7 Symbol Mapping and Channel Observations The beginning of Section 2.6.3 briefly stated that the output bits of a convolutional code are mapped to a symbol and that channel observations are used in decoding. This section addresses symbol mapping and channel observations in detail and develops expressions for the probabilities in (14). After encoding, the n bits in u, and p, are mapped to a point in the signal set. Each of the M = 2" symbols in a signal set represents a distinct group, or label, of b bits. It is possible that b # n, in which case u, and p, will either be spread over multiple symbols (b < n) or be only part of one symbol (b > n). In most cases, b is a multiple of n. CHAPTER TWO 32 The following two sub-sections discuss symbol mapping and decoding under 4QAM and under higher spectral efficiency modulations for b n. 2.7.1 4-QAM Modulation Each signal point in a 4-QAM signal set represents two bits. The first bit corresponds to the x-coordinate, and the second bit corresponds to the y-coordinate. Thus, 4-QAM only supports rate-1/2 convolutional codes. In this case, b = n, and the number of observation points N equals the number of time indices r in a block. 01 * -1 * 11 1- 0 1 -1- 00 0 10 Figure 2-7. Signal points and labels for 4-QAM An observation R = (rr, ry ) is a point in two-dimensional space. In the first probability on the right side of (14), we notice that the x- and y-coordinates are uncorrelated Gaussian variables, given u, =i,S,_ = m',S, =m. Thus, (14) can be re- written as Pr(RIu, = i,S,_, = m',S, = m)= Pr(r ,|u, = i,S,_1 = m',S, = m) Pr(r,|, = i, S,_, = m', S, = M). (15) BACKGROUND 33 From the condition u, =i,S,_, =n',St = i, we know the trellis branch at time t and, thus, the parity bit p, for that branch. If P =(P,, P is the transmit symbol mapped by u, and p,, then the two probabilities on the right side of (15) are Pr(r, I = i, S,_ =i', S,= ) = 2 -T' 12 (16) and Pr(r, l u, = i, S,_ = m', S, = M)= e- 29 (17) where o2 is the channel noise variance per real dimension. 2.7.2 Higher Spectral Efficiency Modulations Equations (15) through (17) do not hold for modulations with spectral efficiencies higher than 4-QAM. There are only two coordinates per symbol representing more than two bits per symbol. Regardless of whether b = n or b > n, we still need information for each of the b bits. The way to generate this information is to calculate a log likelihood ratio (LLR) for each bit in a symbol [6]. For the purpose of illustration, suppose that the convolutional encoders have rate1/2 and that b = 2n = 4 bits. The Turbo encoder generates 1 information bit and 1 parity bit each time index t. The outputs of the Turbo encoder are arranged in the order u,, ,,t U,,, p,, and are mapped to a transmit symbol from the 16-QAM signal set shown in Figure 2-8; there are N = r /2 symbols over the period t = I ... r. Each observation now corresponds to 4 bits and two trellis branches. 34 CHAPTER TWO To calculate a bit LLR, we begin by observing that noise in the AWGN channel is one-dimensional, and the noise samples that move the x- and y-coordinates are independent. Further, the first b/2 bits of each point are associated with the x- coordinate, and the last b/2 bits are associated with the y-coordinate. These facts allow us to consider only the x-coordinate when calculating the LLRs for the first b/2 bits and to consider only the y-coordinate when calculating the LLRs for the last b/2 bits [6]. 1110 1010 1111 1011 0101 1101 1001 0100 1100 1000 0 S 0010 0110 0 0 0011 0111 0 0 0001 0000 0 0 0 S Figure 2-8. Signal points and labels for 16-QAM The formula [4] for calculating the LLR of the j1 h bit L(b) is le L(bj) n 2U.2 b and 2 P1 :b =1 Ye-0 P0~b =0 for J <-, 2U 2 (12 (18) BACKGROUND 35 35 BACKGROUND L(b)= In P1:bj =1 for j b . 2 (19) I):h =0 In equations (18) and (19), T is the channel noise variance per real dimension; R = (rx, r, ) is the observation point; P and P, are the x-coordinates of points where b, is I and 0, respectively; and P,, and P. are the y-coordinates of points where b is I and 0, respectively. As shown mathematically in (18) and (19), the LLR is a measure of the likelihood that Gaussian noise moved the transmitted point from points where b I versus from points where b, =0. The probability Pr(RIu, =i,S,_, =m',S = m) can no longer be separated as shown in (15). Instead, the observation point is now separated according to the part of R, that represents systematic bits u, and the part that represents parity bits p,. Denote the systematic part as R t and the parity part as RP. Using these two variables, the analog to equation (15) is Pr(R,|u, = i, S, 1 = m', S, = m)= Pr(R;' u, = i, S,_1 = n', S = m)- Pr(RP|u,= i, S,_ (20) = m', S, = m) . Using the LLRs from (18) and (19), the probabilities in (20) are calculated as Pr(R; |u, =i,S,4 =m',S, =in)= t hle E11 ~b (21) bb) i -- 6eL~i and rHeb)L(b) Pr(RIu, =i,S, - = m',S, = m)= biep E I+e~b (22) ) 36 CHAPTER TWO The bits b in (21) correspond to u, and the bits in (22) correspond to the parity bits p, for the trellis branch given by the condition. Equation (21) assumes mutual conditional independence for the bits in u, , and thus, their individual probabilities are multiplied. Equation (22) makes the same assumption for p,. The received coordinates r, and r, in (15) can be analytically expressed as a transmit value plus Gaussian noise. Further, it is certain that they are probabilistically separable. In contrast, the parts R;' and R,' in (20) do not have a general analytical form. The most we can say is that R,' is a function of LLRs in the set that R,' is a function of LLRs in the set t L(b,) b1 e p, }. { L(bJ) b1 e u, } and Equation (20) assumes that R;' and R, are conditionally independent even though they may not be [6]. Even so, assuming independence greatly simplifies the calculations and does not noticeably degrade decoding performance [6]. Thus, conditional independence is always assumed. It has been noted [6] that the higher spectral efficiency modulation equations in this section can be used for decoding 4-QAM modulation without much degradation in performance. Thus, the equations in this section are used for decoding all types of modulation. 2.8 The Turbo Code It has been noted that code concatenation and interleaving can achieve lower error probabilities [5]. The Turbo code uses both of these concepts. BACKGROUND 37 2.8.1 The Turbo Encoder The basic structure of the Turbo encoder is formed by a parallel concatenation of two RSCCs. The input to one convolutional encoder is a sequence of information bits, and the input to the second convolutional encoder is an interleaved sequence of those bits. Then, in a process called puncturing, some of the parity bits from the two encoders are selectively used while others are discarded. Puncturing allows the overall rate of the Turbo code to be conveniently tailored. In Figure 2-9 below, the outputs of the RSCC encoders show only the parity bits. The two convolutional encoders are usually the same, although they do not need to be. If the convolutional codes have rate-k/n, then u, represents k bits and p, represents (n-k) bits. ULt Lit t= :pit( encoder 1 puncturing mechanism intereaer Pt Ut , RCC encoder 2 010101... 101010... Figure 2-9. Turbo encoder structure The output bits of the Turbo encoder are mapped to a transmit symbol and sent across the channel, as discussed in Section 2.7. Using the channel observations, bit LLRs are calculated using equations (18) and (19). Before the parity bit LLRs are used by the Turbo decoder, they first need to be un-punctured to reverse the puncturing process from CHAPTER TWO 38 the Turbo encoder. During un-puncturing, a LLR = 0 is inserted in place of a punctured bit. In the absence of any information about the punctured bit, it is equally likely to be a zero or a one, producing a LLR of zero. 2.8.2 Iterative Decoding and the Turbo Decoder The Turbo decoder consists of two MAP decoders, with each one used for decoding one of the two convolutional codes in the Turbo encoder. The APP depends primarily on calculating the y parameter, which is composed of the channel observation probability Pr(R, I u, = i, S,_, = m', S, = M) and an a priori probability Pr(S, = m|S,_, = M') = Pr(u, = i). From equations (21) and (22), we see that the channel observation probability is calculated from bit LLRs. A LLR tell us two things about a bit - the sign indicates whether the bit is zero or one, and the magnitude of the LLR indicates the degree of certain that the sign is correct. A very negative LLR indicates high certainty that the bit is zero, whereas a slightly positive LLR indicates low certainty that the bit is one. Thus, the magnitude of a LLR is called the reliability of a bit decision. The LLR is called soft data because it provides information for making a bit decision and for determining the reliability that decision. The MAP decoder is called a "soft-in soft-out" decoder because it uses soft data input and generates soft data output. Expanding the y parameter in equation (9), the APP can also be written as Pr(ut = i R n] = 112 K,,,, Pr(R, u, = i).Pr(u, = i). (23) Pr(R, I|u = i, S,_i = m', S, = m)Pr(u, = i|St, = m', S = m).a,, (m'),, (M) BACKGROUND The right side of equation (23) has three parts: 39 the systematic channel observation probability Pr(Rs u, = i) the a priori probability Pr(u, =i), and the last part called the extrinsic information [2]: E Pr(R' |u, = i,S,_1 = m', S, = m)Pr(ut = i|S,_1 = m', S, = im). at,(m')t (m) Pr(ut =i RN Pr(Rt Iu, = i)- Pr(u, = i) From the extrinsic information, we define the extrinsicprobability P,- < =Kex as Pr(ut =i RN Pr(RIu, = i) Pr(ut= i) (25) The factor Kx, normalizes the sum of the extrinsic information across all i to unity. The extrinsic probability is generated by the MAP decoder and is also soft data. For a time t, the probabilities P,"C correspond to the values of u, =i, and the magnitude of each P," indicates the certainty of that value of i. Thus, the extrinsic probability provides soft information about u, = i and can be used as a prioriinformation. The Turbo decoder has two MAP decoders, and the extrinsic probability at the output of each MAP decoder is used as the a priori probability in equation (23) in the other MAP decoder. The point of the extrinsic information is to pass information to the other MAP decoder that it does not already have. Notice that the extrinsic information produced by each MAP decoder is a function of the parity bit information for that decoder. As a result of the puncturing process, each MAP decoder does not possess the parity bit information in the other decoder. Thus, the extrinsic information produced by CHAPTER TWO 40 one decoder supplements the information in the other decoder. Further, equation (24) shows that the systematic channel observation probability Pr(R, Iu, =i) and the a priori probability Pr(u, = i) are not part of the extrinsic information. Both MAP decoders already possess the systematic channel observation, and the a priori probability used in one decoder is just the extrinsic probability generated by the other decoder. Thus, Pr(RsIu, = i) and Pr(u, = i) are correctly excluded from the extrinsic information. The entire process described above is called iterative decoding and is illustrated below in Example 2-1. Pr (u=i) t Pr (u'=i) P LfL( : 0nee L(u) e MAP decoder 2 MAP decoder 1 1 L(p ) L(p2) APP' APPI mFximum APP decision Figure 2-10. Turbo decoder structure Example 2-1. Iterative decoding using extrinsic probabilities Iteration 1 * MAP decoder I has no a priori input during the first iteration, so it uses Pr(u, =i)= 2 -k in equation (23) to calculate the APP and generates P eli using equation (25), for all i = 0, ... , 2 k-' and t =1, ... ,r. 41 BACKGROUND MAP decoder 2 uses the time-interleaved sequence Pr(u,. = i)= P," to calculate the APPs: in equation (23) Pr( ut, = i RN )= Pr(R u, = i)- P" . IIPr(R~t'ut,= i, St,,_, =m', St,. = m)Prfut,. = i|S,_, =M', St,. = M)- a,._(M'),.t,(M) MAP decoder 2 generates t,< using (25) for all i and t'. Here, t' refers to an interleaved sequence with respect to t. Iteration 2 and beyond - MAP decoder I uses the de-interleaved sequence Pr(u, calculate the APPs: Pr(u = i RN YJ =Pr(R;| u,= t = i)= Pt22 in equation (23) to . Pr(R,u, =i,S_ =m',S, m)Prfu, = i|S,_1 = m', St = M). a,_] (M'),t(M) MAP decoder I generates P,'' using (25) for all i and t. MAP decoder 2 uses the time-interleaved sequence Pr(u,. =i)= P,<' in equation (23) to calculate the APPs: Pr(u,. =i RN )=Pr(RiIu,. = i). P t i Pr(RmtIu,. = i, S,._, = m', S,, = m)Pr(u, =|_, = m' MAP decoder 2 generates p, 2 ,i using (25) for all i and t'. interleaved sequence with respect to t. , -I _ ',(M) Here, t' refers to an 2.8.3 Fast Decoding of Rate-1/n Component Encoders With minor modifications, we can reduce the amount of computation for MAP decoding of rate-I/n convolutional codes. There is only one information bit, so k = I and there are only two APPs. Define the quantity A to be the natural log of the ratio of the two APPs : 42 CHAPTER TWO Pr(u, =1 RN A(u )=ln ' Pru, =01 R, -N(m')y, 1 (R,,m,m),(m) In (26) 0(R,,Inm)'1 (mn) a,- (y Expanding the y parameters in (26) the same way as in (23), equation (26) becomes A(u, )=L + L' + L where u 1 =i) L' = In P Pr(R, u, =0) and L' =In Pr(u ) Pr(u, = 0) Then, the extrinsic information L is calculated by Et = A(u )-Lt - L' (27) which is analogous to (25). Using equation (26), the decoded bit corresponds to the sign of A rather than the maximum APP. For the case of rate-1/2 RSCCs, using equation (27) instead of equation (25) to generate extrinsic information results in faster decoding because the operations in (27) are additive rather than multiplicative. Further, equation (25) generates two extrinsic probabilities, one for each value of i, while equation (27) only generates one extrinsic value. Using these new quantities, the iterative decoding process for rate-1/2 component codes is illustrated in Example 2-2. BACKGROUND 43 Example 2-2. Iterative decoding with rate-k/n (k > 1) component RSCCs Iteration 1 MAP decoder I has no a priori input during the first iteration, so it uses Pr(u, = i) =0.5 in the y terms in equation (26) and generates L, using equation (27), for all t = l,..., r . MAP decoder 2 uses the time-interleaved sequence K, to calculate the a priori probabilities in the the y terms in equation (26) as follows: Pr(u, = i) = I +e MAP decoder 2 generates L, using (27) for all t'. Here, t' refers to an interleaved sequence with respect to t. Iteration 2 and beyond - MAP decoder I uses the de-interleaved sequence Lt to calculate the a priori probabilities in the the y terms in equation (26) as follows: j2e Pr(u, = i) = ueLt M+ e ' MAP decoder I generates Lt using (27) for all t . MAP decoder 2 uses the time-interleaved sequence L, to calculate the a priori probabilities in the the y terms in equation (26) as follows: Pr(u, =i)= e MAP decoder 2 generates L sequence with respect to t. I+ e 4 using (27) for all t'. Here, t' refers to an interleaved CHAPTER THREE THE SIMULATION TOOL The purpose of the tool is to simulate Turbo codes described in the various ITU contributions. The tool is designed for that specific purpose and will have certain limitations that make it unsuitable for other applications. These limitations will become apparent in the sections that follow. 3.1 Simulation Tool Scope The main Turbo code contributions considered in designing the tool include ITU documents CF-037 [15], CF-072 [17], RN-027 [20], RN-079 [18], and IC-024 [19]. The Turbo encoder proposed in CF-037 is the standard structure in Figure 2-9, while other companies have proposed the variations shown in Figures 3-1 through 3-3. -45 - CHAPTER THREE 46 interleaer 2 RSC encoder 1 '' Ut t --mechanism = ?. U"t puncturng ... t010101... 101010 ... RS(r 32 UF encoder2 I Figure 3- 1. Turbo encoder from RN-027 , Ut 4 RSCC encoder t 1 t punctung mechanism interleaver 11 100100 ... Ut intede;Der 2 n1c de 3S Pt 010010 ... 001001 ... RS~C encoder 2 - Ut p3 t Figure 3-2. Turbo encoder from CF-072 Ut interleaer 0 re-3A4 t Figure 3-3. Turbo encoder from RN-079 The different Turbo encoder structures include parallel concatenations of more than two RSCC encoders and also serial concatenation of RSCC encoders. The Turbo decoder structures that correspond to the encoders in Figures 3-1 through 3-3 also differ THE SIMULATION TOOL 47 from the standard structure in Figure 2-10; the differences are straightforward and are not shown here. Aside from structural differences, the proposed Turbo codes also have component differences; one Turbo code may work especially well with one type of interleaver or RSCC encoder while other codes may use other types. Thus, the simulation tool needs to accommodate both structural and component differences for different Turbo codes. 3.2 Design Considerations for Structural Flexibility The simulation platform is designed to be highly flexible, requiring minimal code changes to simulate different Turbo code proposals. As mentioned in the previous section, the design should account for structural flexibility and component flexibility. Structural flexibility is addressed first in this section. 3.2.1 Simulation Tool Language Figures 2-9, 3-1, 3-2, and 3-3 reveal that the Turbo code is composed of several independent blocks connected via flow lines. To implement a Turbo code, we can simply execute each block, or module, in order by following the direction of flow lines. Although the modules operate independently, data in the form of bits or real values are still passed between them. Thus, there is need for a system to define connections between modules and to implement data transfer between them. The needs of the system fit the description of object-oriented programming (OOP). However, OOP requires the use of syntax not supported by the compilers prevalent on most workstations. Thus, the simulation tool is implemented using regular 48 CHAPTER THREE procedural programming in ANSI-C. Such an implementation still adheres to the design goals above without any loss of flexibility. The only direct disadvantage of not using OOP is slightly worse module abstraction and program structure, which does not affect simulation performance. 3.2.2 Module Flow Implementation Using ANSI-C, each module can be implemented as a struct, with flow connections specified by input and output fields: struct basic-module { flow type* input; flow-type* output; Code 1. Module template Each module must have the input and output fields in code 1, and both fields must be of the same variable type. This system guarantees that any two modules can be connected together regardless of whether their connection might make sense at the present time. Using this system, module flows can be specified by directly equating inputs and outputs: encoderl.input interleaver.input encoder2.input puncturer.inputl puncturer.input2 = = = = = bitSource.output; bitSource.output; interleaver.output; encoderl.output; encoder2.output; Code 2. Module flow connections THE SIMULATION TOOL 49 The flow implementation in code 2 offers high structural flexibility because modules are linked by simply defining input and output connections. Thus, modules can be inserted or deleted with ease. 3.2.3 Data Transfer between Modules Each module's input and output point to a specific memory location. By equating inputs and outputs as shown in code 2, we are actually assigning the memory locations of the input pointers. However, the memory locations of output pointers are not specified. The flow connection implementation requires each module to allocate memory for its output pointer internally, while input pointers are assigned externally. When a module places data at the memory location specified by its output pointer, it is in effect transferring data to the input of another module. Thus, each module can take data from its input pointer and write resulting data to its output pointer without having to specifically communicate with other modules. Simply executing the modules in order of flow direction will transfer data through the encoder and decoder structures. The next design issue is to determine how data is organized at each module's input and output memory locations. Specifically, the generic flow-type designation in code 1 needs to be replaced with an appropriate data structure type. Consider that data transferred through the Turbo code is ordered with respect to time and that the actual amount of data that is transferred can vary depending on the types of components in the Turbo code. Thus, the data structure must be ordered and must allow data elements to be easily added or deleted. requirements is a linked list. The most appropriate structure for implementing these 50 CHAPTER THREE head next -3. 1 01 004 tail Figure 3-4. Linked list A linked list is a chain of elements, with the first element called the head of the list and the last element called the tail of the list. Each element contains a pointer to the previous element in the list and a pointer to the next element in the list. The head has no previous element and the tail has no next element. The next and previous pointers allow the linked list to be ordered, and elements can be easily added to or deleted from the list by changing those pointers. Each element also contains variables to hold data. 3.2.4 Module Execution The previous sections have created a system for making directional connections between modules and for transferring data between modules. The only task that remains is to execute each module in the correct order and allow the data to automatically flow through the Turbo code structure. The lines in code 3 perform Turbo encoding according to the structure in Figure 2-9; the variables are the same as those used in code 2. Each function accepts a struct and uses the variables in the struct to perform its task. The function reads data from the module's input linked list, and resulting data is placed in the output linked list. The order of the function calls correctly corresponds to the flows in Figure 2-9 and to the connections made in code 2. While this ordering can THE SIMULATION TOOL 51 be automated solely from the flow connections defined in code 2, doing so would likely detract from the run-time efficiency of the tool. Further, the user can order the functions quickly and easily, making the automation feature unnecessary. generate bits(bitSource); doencode(encoderi); terminate(encoderi); do interleave (interleaver); doencode (encoder2); do puncture(puncturer); Code 3. Turbo encoding 3.3 Design Considerations for Component Flexibility Section 3.2 created a framework for implementing structural flexibility and for module execution. This section now addresses considerations for component flexibility. Component flexibility is based on two factors: 1) the comprehensiveness of module definitions, and 2) the amount of code changes necessary to create different types of the same module. The aim is to maximize factor one while minimizing factor two. Factor one depends on the variable definitions in a module struct. For the purpose of this simulation tool, factor one can be sufficiently satisfied if the module is capable of taking on the forms specified in the Turbo code proposals listed in Section 3.1. However, when obvious extensions of those forms do not unreasonably complicate module implementation, variable definitions are made intentionally broad to cover those' extensions. Factor two depends on module implementation and can rely in part on industry standards. To minimize the amount of code changes, the user should only need to specify numerical or string parameters for a module, and the module should create the data 52 CHAPTER THREE structures it needs from those parameters. Thus, factor two is mainly concerned with module initialization. The following sections will discuss design and implementation considerations for all modules available in the simulation tool. Code for these modules are shown in Appendix C. 3.3.1 Linked List Design and Implementation The linked list can itself act as a module for generating bits, operating as a bit source. The theory of a linked list as described in Section 3.2.3 is quite general, and the data structure can be implemented in *a variety of ways. One simple implementation is to create an array of elements and to keep track of the number of elements in the list. The next and previous pointers of each element are implemented as integer values corresponding to the array index of adjacent elements in the list. Although this implementation is straightforward, there are two obvious drawbacks. The first is that we must be sure to allocate enough array cells to hold the maximum possible number of elements, and this number may not be available during module initialization. The second drawback is that the list will not always hold the maximum number of elements, and memory allocated for empty slots in the array is wasted. The use of dynamic memory allocation and deallocation solves both of these concerns. typedef struct linkListStruct valueLength; int listElement *head, *tail; struct linkListStruct *output; } linkList; Code 4. Linked list definition THE SIMULATION TOOL Code 4 shows the linked list definition used by the simulation tool. 53 During module initialization, both the head and tail pointers are set to NULL. When the first element is added to the linked list, memory for the new element is allocated and both the head and tail point to the same element. As elements are added or deleted, memory for those elements are allocated or de-allocated, respectively. Thus, there is no need to know the memory requirements during module initialization, and memory is not wasted. Each list element is designed to correspond to a specific time index. During a time index, an element may need to store multiple bits or multiple signal points, so each list element is designed to hold an array of bits and an array of two-dimensional Cartesian coordinates. valueLength The lengths of the arrays are the same and are specified by the variable in code 4. Thus, all elements of a linked list are implemented with the same data array lengths. Lastly, during module initialization, the output pointer is set to point back to the linked list itself. The reason for this redundancy is simply to allow the linked list itself to be used as an input for module connections, as shown in code 2. 3.3.2 Interleaver Design and Implementation The interleaver function is relatively simple to implement. An interleaver takes a sequence of elements and rearranges its order according to a permutation pattern, which is implemented by a lookup table in the simulation. It is important to note, however, that because of memory constraints in real-life systems, an actual interleaver implementation would be algorithmic and would not be a lookup table. 54 CHAPTER THREE Recall that each MAP decoding process works with a block of bits corresponding to trellis branch transitions. The number of branch transitions is the same as the number of time indices and depends on the rate of the convolutional code. Thus, the length of the input sequence is the same as the number of time indices in a block of bits and depends on parameters from other modules. Unlike sequence length, the permutation pattern is unrelated to other module parameters. However, initializing the variable is not completely straightforward. The permute pattern is generated algorithmically during module initialization, and new initialization functions need to be written for new algorithms. ITU proposal RN-029 [21] describes a two-step semi-random algorithm for generating a permutation pattern, and the algorithm has been implemented in this tool. Algorithms described in other proposals [15] may use permutation matrices instead of a permutation pattern in array format. Even so, the effect of the matrix is to rearrange the order of the input sequence, and that operation can always be described by a permutation pattern [13]. Thus, some permutation algorithms may need to be adapted to the array structure used in. the interleaver module. Variables for the interleaver module are shown below in code 5. typedef struct interleaverVarsStruct int blockSize; int* permutePattern; linkList* input; linkList* output; interleaverVars; Code 5. Interleaver module definition THE SIMULATION TOOL 55 3.3.3 Convolutional Encoder Design and Implementation Every convolutional encoder structure takes in some number k of input bits and generates another number n > k of output bits. In doing so, the encoder stores v bits in memory registers that are each a linear sum of past input and output bits. In a RSCC, k of the n output bits are exact replicas of the input bits, and the other r = (n - k) are parity bits. Thus, a RSCC has rate-k/(k+r). 3.3.3.1 Use of the Trellis Diagram for Encoding Convolutional codes can have very different encoder structures, as shown in Figures 2-2 and 2-3. However, the similarity among all convolutional codes is that their behavior can be mapped out in a trellis diagram, and the parameters k, r, and v, determine certain characteristics of the diagram. A trellis diagram has 2' states, with 2k branches entering each state and 2 branches exiting each state. Each branch corresponds to a unique k-bit input and also to a r-bit parity number. In real-life systems, a VLSI implementation of a convolutional encoder is easy and fast, and allowing the encoder structure to be used for encoding. However, for the simulation tool, using a trellis diagram to perform convolutional encoding is much more efficient than using the encoder structure. A trellis branch can be calculated by using a kbit input with an encoder starting state and by recording the resulting state and parity bits. Only 2 k* branch calculations are required to construct a trellis diagram. Then, to perform encoding, we simply traverse the trellis by following the correct branches. In contrast, using the encoder structure to perform encoding requires one branch calculation CHAPTER THREE 56 per time index, and the number of time indices is greater than 2 k+ by several orders of magnitude. As shown in code 6, the trellis description is stored in a two-dimensional array. The first dimension has 2' indices and corresponds to the encoder states. The second dimension has 2k indices and corresponds to the branches exiting a state. Each branch stores the starting and ending states, as well as input and parity bits. typedef struct branch** int* char* encoderVarsStruct trellis; terminator; state; int k; int int r; V; linkList* linkList* encoderVars; input; output; Code 6. Convolutional encoder module definition The one-dimensional array terminator describes the path from any state to the zero state. The index of an array cell is the current state, and the content of the array cell is the k-bit input corresponding to the next branch to take. The general rule for terminating a convolutional code with v delay registers is that v input bits are required to complete the termination. If the code has rate-k/(k+r), then those input bits correspond to v/k branches. However, since k may not be a multiple of v, the simulation tool uses -] -k bits to perform the termination. k THE SIMULATION TOOL 3.3.3.2 57 Generating Polynomials As described in Section 2.6, the output bits of a convolutional encoder are formed from a linear sum of current and past input and output bits. For k input bits uo ... Uk], v memory registers, and r output bits yo ... Yr-I, k-Iv 010 t i=0 j=0 a"i (t j) + i=0 J=I (28) b" y(t-j). is the coefficient for calculating output bit y,, using input bit u with delay j. Similarly, b"' is the coefficient for calculating output bit y,, using output bit y, with delay j > 0. The binary coefficients a' and b"' determine the encoder connections shown in Figures 2-2 and 2-3. For a bit b, +b = -b. Thus, (28) can also be expressed as r-I v Y, (t)+ Y i=0 j=I k-I v b|"yi(t - j)= i=0 j-0 a,"' ui(t - j)j. (29) Then, taking the D-transform of (29) and rearranging, we have k-I y,, (D 1 + JDj ) +0 The sequences a,'=ia'0... a'" Y(D ( b7jD j=0 = and b"=( b'. .. bt"' a,"'JD ut(D i=0 J- (30) 0 are referred to as generating polynomials because they determine the terms of the delay polynomials in (30) that generate the output bits. For clarity, the sequences a,"I will be referred to as feed-forward polynomials, and b"' will be referred to as feedback polynomials. The convention for describing a convolutional code is to express the feed-forward and feedback polynomials as octal numbers. The most significant bit (MSB) of the feed- 58 CHAPTER THREE forward octal number is a", corresponding to no delay, and the least significant bit (LSB) is a.", corresponding to highest delay. The feedback polynomial follows the same delay order. Using octal notation for the encoder in Figure 2-3, the feed-forward polynomials for the parity bit are ao = 35 and ao = 278, and the feedback polynomial is bo = 318. Following the connections for the encoder in Figure 2-2, the generating polynomials are not immediately obvious. The output is not fed back, and the recursive connections seem to make the feed-forward polynomial infinitely long even though there are only four delay registers. In this case, the polynomials can be obtained by analysis: So(D)= u (D)+ So(D)(D' + D4) u (D) 1+ D' + D4 pO(D)= S,(D)(1+ D + D2 + D4) PO(D)= + (+D 4 (I+D+D2+D4) pO(D)(+D'+ D4)=u,(D)(1+D+D2 +D4) Thus, the feed-forward polynomial is ao = 35,, and the feedback polynomial is bo = 23 3.3.3.3 Constructing the Trellis Diagram Although every convolutional code can be fully described by its generating polynomials, implementing the codes can require very different encoder structures, i.e. the placement of memory registers and bit adders, and the direction of feed-forward and feedback paths. As an example, the convolutional codes of Figures 2-2 and 2-3 can both be THE SIMULATION TOOL 59 mathematically described by their generating polynomials, shown in Section 3.3.3.2. Even so, the encoder in Figure 2-2 is specifically designed for a rate-1/n RSCC, and the encoder in Figure 2-3 is specifically designed for a rate-k/(k+1) RSCC. It is difficult to imagine how the generating polynomials for the code in Figure 2-3 would be implemented on the structure in Figure 2-2. Thus, constructing a trellis diagram requires knowledge of the encoder structure in addition to the generating polynomials. In fact, it is interesting to note that the structures in Figures 2-2 and 2-3 can both be used to implement a rate- 1/2 RSCC and that the two implementations produce different trellises for the same generating polynomials. This confirms that a trellis diagram depends on knowledge of both the generating polynomials and the encoder structure. When constructing the trellis, the simulation tool stores generating polynomials in two separate variables. array. Feed-forward polynomials are stored in a three-dimensional The content of each array cell is a coefficient a,', with the first array index corresponding to output bit p,,, the second to input bit u,, and the third to delay j. Feedback polynomials are stored in a one-dimensional array, with the array index corresponding to delay j. The convolutional codes encountered thus far in the ITU Turbo code proposals only generate one parity bit, so the simulation tool assumes that only one feedback polynomial is needed. Consequently, only the rate-1/n and the rate-k/(k+1) RSCC encoder structures in Figures 2-2 and 2-3, respectively, are implemented in the tool. The rate-1/n structure is only used to implement rate-1/2 codes. The trellis diagram for RSCCs can be constructed using those two structures, but new code must be written to implement other types of encoder structures. Of course, the rate-1/n structure can be 60 CHAPTER THREE used for n > 2, but the feedback array must be modified to hold more than one polynomial. 3.3.4 Puncturing / Un-Puncturing Module Design and Implementation The task of the puncturing module is to adjust the overall rate of a Turbo code by selectively discarding parity bits according to a set pattern. Regardless of how many linked lists are at the input of the puncturing module, there is only one output list. The job of the un-puncturing module is to perform the reverse operation and to generate multiple' outputs from one input list, also according to the same pattern. Thus, the puncturing module and its counterpart have nearly identical definitions. typedef struct punctureVarsStruct numInputs; int int punctureLength; currIndex; int char** puncturePattern; linkList** input; linkList* output; punctureVars; Code 7. Puncturing module definition The pattern for each input list is stored in a one-dimensional array, and all of those patterns are stored in another one-dimensional array. Thus, the patterns for all inputs are stored in a two-dimensional array. The puncturing patterns are accessed circularly, and the puncturing module will work for any number of input bits. In general, though, the number of parity bits per input will be a multiple of the pattern length. Where the puncturing module works with bits, the un-puncturing module works with log-likelihood-ratios. When performing un-puncturing, a discarded bit is replaced THE SIMULATION TOOL with LLR = 0, indicating that the bit is equally likely to be zero or one. 61 Since the puncturing module will work for any number of input bits, the stop condition for unpuncturing must make certain that the number of LLRs at each output of the unpuncturing module is at least the number of parity bits at each input to the puncturing module. Consider the two puncturing patterns below: Pattern index input 1: input 2: 1 Pattern 2 0123 012345 1010 0101 010000 000010 In pattern 1, the un-puncturing process can stop when no more LLRs remain at the input to the un-puncturing module, which can occur at any index. However, the same condition will not suffice for pattern 2. Suppose that the final LLR input is used at index I in pattern 2. According to the pattern I stop condition, un-puncturing would end at index 1. However, it is possible that the puncturing process actually ended at index 2 or index 3, which would not produce any more parity bits beyond the final bit at index 1. Thus, the un-puncturing process must not end at index 1. The problem is that the un- puncturing module does not know whether to end at index 2 or index 3. It only knows that un-puncturing must end before index 4, when another LLR input is required. Without any more information, we must assume that having more LLRs than are actually required is better than having less. Thus, the most appropriate stop condition to use when performing un-puncturing is ending when the next pattern index requires an input LLR and none remain. 62 CHAPTER THREE typedef struct unPunctureVarsStruct int numOutputs; punctureLength; int int currIndex; char** puncturePattern; linkList* input; linkList* output; unPunctureVars; Code 8. Un-puncturing module definition The only main tasks that need to be performed when initialization the puncturing and un-puncturing modules are assigning the puncturing patterns to the two-dimensional array puncturePattern and allocating the correct number of input and output linked lists. 3.3.5 Signal Mapper Design and Implementation A signal mapper has the task of assigning information bits and parity bits from a Turbo encoder to points in a signal set. This task can be performed in two steps. The first step takes the correct number of information and parity bits from the module's inputs and orders those bit according to a set arrangement. The second step takes the arranged bits and finds the signal point with a matching label. This process is described in Section 2.7. The only information needed to perform the first step is the bit arrangement order, which is specified during module initialization. The simulation tool assumes that if bits generated from different time indices are grouped together, bits that are generated earlier will be placed before bits that are generated later. Since the input information and parity linked lists are already ordered with respect to time, taking the bits sequentially from those lists automatically satisfies the ordering assumed by the tool. The arrangement THE SIMULATION TOOL order is stored in a one-dimensional array in which a 's' 63 character specifies an information bit and a 'p' character specifies a parity bit. In step two, mapping the bit grouping from step one to a signal point is straightforward and can be implemented using a lookup table. The lookup table consists of an array of signal points and array of point labels, and the same indices in the two arrays correspond to a point-label pair. The signal points and labels are generated during module initialization. The simulation tool currently uses two-dimensional coordinates for signal points, and new code must be written for other coordinate types. Further, the tool has only implemented the square-QAM signal set, and normal and gray labeling. New code needs to be written for other types of signal sets or labeling schemes. The module definition is shown in code 9. The variable bitMapper holds the bit arrangement order needed for step one, and the variable symbolMapper holds the signal set and point labels needed for step two. typedef struct twoDsignalMapVarsStruct bitMapVars bitMapper; twoDsymbolMapVars symbolMapper; } linkList* inputU; linkList* inputS; linkList* inputP; linkList* output; twoDsignalMapVars; Code 9. Signal mapper module definition As shown in code 9, the signal mapper module is also designed to accommodate uncoded bits. For the bit arrangement order stored in bitMapper, the character 'u' specifies an uncoded bit. However, the simulation tool does not currently use uncoded bits. 64 CHAPTER THREE 3.3.6 Channel and Demodulator Design and Implementation The channel model used by the simulation tool is the AWGN channel, which is described 2 by noise variance O-N . As described in Section 2.4, the noise variance can be calculated using equation (8), which is shown again below for convenience. N = E,2q E' No Es, the average energy per symbol, is stored in the symbolMapper variable of the signal mapper module defined in Section 3.3.5, and 7, the number of information bits per symbol, is stored in the bitMapper variable of the same module. E INO is defined in the main simulation program. typedef struct channelVarsStruct double n variance; linkL ist* input; linkList* output; } channelvars; Code 10. Channel module definition The simulation tool has only implemented the AWGN channel, and new code needs to be written for other types of channels. The task of the demodulator module is to calculate bit LLRs from observations at the output of the channel module. This process is described in Section 2.7.2, and LLR equations are given in (18) and (19). In calculating bit LLRs, the demodulator module uses information from the signal mapper module and from the channel module. This information is not stored in the demodulator module itself and is passed in to the module function. Although the use of external information in performing the module function THE SIMULATION TOOL 65 violates module abstraction, making the information internal does not offer any added benefits from a simulation perspective. Thus, the module was designed to use external information, and its definition does not contain internal variables. typedef struct demodulatorVarsStruct linkList* input; linkList* outputS; linkList* outputP; demodulatorvars; Code 11. Demodulator module definition Section 2.8.1 states that the decoding modules will always use bit LLRs to perform decoding. Thus, the demodulator module will always output LLRs, and as the next section will show, the MAP decoder module will always assume that its information and parity input lists contain LLRs. 3.3.7 MAP Decoder Module Design and Implementation The main tasks of the MAP decoder module are to produce the a posterioriprobabilities given in (9) and to generate extrinsic information for the iterative decoding process. The MAP decoding algorithm is described in Section 2.6.3, and the extrinsic information is shown in equations (25) and (27) in Section 2.8.2 and Section 2.8.3, respectively. As shown in 2.6.3, producing the APPs requires calculating the a, fl, and y parameters shown in equations (12), (13), and (14), respectively. Further, the y parameter must be calculated first since the recursions in (12) and (13) depend on this parameter. The parameters at (m) and ,(in) are state parameters and each is stored in a two-dimensional array, with the first dimension in time t and the second in trellis state m. 66 CHAPTER THREE The gamma parameter y (R,,m',m) is stored in a three-dimensional array, with first dimension in time t, second dimension in information bits u, = i, and third dimension in parity bits p, = j. The value of y depends only on the values of i and j for the information bits and parity bits, respectively. Because the trellis structure from the convolutional encoder is such an integral part of MAP decoding, the decoder module stores its own trellis description. The array bitValue is used in calculating the probability in equation (21) and holds the individual bits corresponding to u, = i. The module definition is shown below. typedef struct MAPdecoderVarsStruct { int blockSize, numStates, numInputs; double explimit; double ***gamma; double **alpha; double **beta; char **bitValue; branch **trellis; linkList* inputS; linkList* linkList* linkList* linkList* inputP; inputA; outputDprob; output-extrinsic; MAPdecoderVars; Code 12. MAP decoder module definition 3.4 Simulation Code Organization The simulation tool is composed of a collection main program files, C header files, and C source files. Each module has a header file, which contains struct definitions and function declarations, and a source file, which contains function code. Module source files primarily include three types of functions - initialization functions, execution THE SIMULATION TOOL functions, and memory de-allocation functions. 67 Each Turbo code structure is implemented in a different main program file. Table 3.1 summarizes the different files. Code for all files are included in Appendices A and C. 68 68 CHAPTER THREE CHAPTER THREE File name turbosim-std.c Description main simulation file for double parallel concatenated Turbo code from CF-037 turbosim-icoding.c main simulation file for triple parallel concatenated Turbo code from CF-072 turbosim-uncoded.c main simulation file for uncoded simulation Module Ifunction(s) Module initialization functions File name general.h general.c general functions linkedlist.h linkedlist.c linked list llistInitVars( ) source.h source.c bit generator module / genBits() selectBlockSize() interleave.h interleave.c interleaver module / doInterleave( ) Random pattern - randomPatterninitVars() Semi-random pattern - SrandomInitVars( ) Get pattern from a file - interleaverFileInitVars() conv.h conv.c convolutional encoder module / encodeBlock() terminateEncoder() Rate-1/2 - rate]2initVars() Rate-k/(k+l) - ratekk]InitVars() Copy an encoder - duplicateEncoder() puncture.h puncture.c puncture module / punctureBlock( ) unpuncture module / unpunctureBlock() 2-input puncturer - twoPuncturelnitVars() 3-input puncturer - threePunctureInitVars() Un-puncturer - unPunctureInitVars() Bit map pattern - bitMapInitVars() QAM signal set - QAMSignalSet() Normal bit labels - normalMappingInitVars() Gray code labels - createGrayCode() mapper.h mapper.c signal mapper module / mapBlock() channel.h channel.c channel module / sendAWGNblock() demod.h demod.c demodulator module / demodulateTwoDBlock() decodeuncoded() demodulatorInitVars() decoder.h decoder.c MAP decoder module I doMAPdecoding() doFastRl2MAPdecoding() MAPdecoderInitVars() channellnitVars() Table 3.1. Summary of simulation files CHAPTER FOUR SIMULATION RESULTS The files and modules shown in Table 3.1 form the entire simulation tool. The tool was used to simulate the Turbo codes from CF-037 and CF-072, and results are compared to those cited in these documents. Both simulation and cited results are shown in the sections below. Simulation results are gathered from running the tool on PCs with Microsoft Visual C++ and on SUN workstations using the gcc compiler. As different compilers and computers generate random numbers differently, the simulated results shown below may not be reproducible in all environments. Even so, results should be similar and BER curves should fall within the same order of magnitude. All simulations used square-QAM signal sets with gray labels, which have been shown to be ideal [8]. The coordinates of the QAM symbols are odd. The all zero label - 69 - 70 CHAPTER FOUR corresponds to the most negative coordinate. The 4-QAM and 16-QAM signals sets are shown below in Figures 4-1 and 4-2. 01 . -1 . 00 11 I . -10 -1 - 10 Figure 4-1. 4-QAM signal set 0010 0110 0 0 0011 0111 0 0 -3 -1 0001 0101 0000 0 0100 3 1110 1010 0 0 1111 1011 S ' i | 1 3 1S -1 1101 1001 1100 0 1000 -3 Figure 4-2. 16-QAM signal set with gray labels 4.1 Results for Double Parallel Concatenated Turbo Code The encoder structure for the Turbo code in CF-037 is that of Figure 2-9. Both of the component RSCC encoders have rate-1/2, and the generator polynomials are ao = 35 and b =23,. The encoders are implemented using the structure in Figure 2-2. SIMULATION RESULTS 71 The simulations used the semi-random interleaver algorithm from [21], which is not same interleaver specified by CF-037. Even so, it has been noted that interleavers of the same length have approximately the same performance, so results should not differ much for the two interleavers. The simulations only used square QAM signal sets. CF-037 is based primarily on the initial proposal [15], which contains the majority of cited results. 4.1.1 Simulation 1: Rate-1/2 Turbo code This simulation uses 1024 bits per block and generator polynomials ao = 35 and b = 238. The signal set is square 4-QAM. Information bit (u) Parity bit (p) Parity bit (p2 ) Bit mapping per symbol u1 p U2 P22 (bo , b1 ) = (u1 ,p (bo , b1 ) = (u2 , 2) Table 4.1. Simulation I puncturing and mapping scheme The values in Table 4.2 are approximations of graph points shown in [15]. Although the values are not exact, they are good guidelines of what the BERs should be. Iteration EWNo 0.5 1.0 1.5 2.0 1 1.00E-01 8.OOE-02 4.50E-02 1.70E-02 2 1.00E-01 5.OOE-02 8.OOE-03 2.50E-04 3 1.00E-01 2.50E-02 1.00E-03 2.50E-06 4 1.00E-01 2.OOE-02 4.OOE-04 8.OOE-07 8. 1.0E-01 1.OOE-02 9.OOE-05 1.1 OE-07 Table 4.2. Approximate BER values from cited graphs for simulation I Turbo code 72 CHAPTER FOUR The data in Table 4.3 shows simulated BER values over the same range as Table 4.2. To produce more accurate BER values, ten times more blocks were simulated for the BERs in the lower part of Table 4.3 than for the upper part. E After 8 iterations at /No =2.00 [dB], there were only 1.95E-07 - 1024E+05 ~ 20 bit errors. In the extreme case that these 20 bit errors all occurred during the first 10,000 blocks, the BER would have been one magnitude higher. Thus, simulating a higher number of blocks for large Eb / No allows the BER to decrease after an error occurs. In order to produce a reliable value for a BER in the order of 10-x, a general rule is to simulate 10,+2 bits. Not all values in Table 4.3 adhere to this rule, however. Iteration Ed/No 0.50 0.75 1.00 1.25 1.50 1 1.16E-01 1.00E-0 I 8.24E-02 6.38E-02 4.58E-02 2 1.04E-01 7.87E-02 4.93E-02 2.32E-02 7.54E-03 3 9.86E-02 6.52E-02 3.03E-02 8.50E-03 1.37E-03 4 9.47E-02 5.64E-02 2.15E-02 4.46E-03 5.1OE-04 8 8.82E-02 4.47E-02 1.30E-02 1.86E-03 1.55E-04 1.75 2.95E-02 1.58E-03 1.06E-04 2.11 E-05 3.78E-06 2.00 1.71 E-02 2.22E-04 5.77E-06 1.21 E-06 1.95E-07 Table 4.3. Simulated BER values for simulation I after 10,000 and 100,000 blocks Cited results in Figure 4-3 and all subsequent plots are marked by square boxes and dotted lines, while simulated results are marked by crosses and solid lines. BER curves for 1, 2, 3, 4, and 8 iterations are shown. Figure 4-3 shows that simulated results match cited results relatively well. SIMULATION RESULTS SIMULATION RESULTS 73 73 1.E+00 1.E-01 1.E-02 - 1.E-03 - _ ___-_ M1.E-04 - - 1.E-05 - - - - -X 1.E-06 1.E-07 - 0 0.5 1 1.5 2 2.5 Eb/No Figure 4-3. Simulation I BER curves for Table 4.2 and Table 4.3 4.1.2 Simulation 2: Rate-1/2 Turbo code The ITU proposal in [20] suggests the use of a rate-1/2 convolutional encoder with generator polynomials a~ =178 and b =58. This encoder is used in place of the one in simulation 1 from Section 4.1.1. All other parameters from simulation 1 remain unchanged. Figure 4-4 shows that for the Turbo encoder structure from [15], using component codes with polynomials a~ =35 and b =238 codes with polynomials ao = 178 and bo = 15. yields better BER performance than using 74 74 CHAPTER FOUR CHAPTER FOUR Iterations Eb/No 0.50 0.75 1.00 1.25 1.50 1 1.02E-01 8.77E-02 7.28E-02 5.78E-02 4.38E-02 2 8.59E-02 6.43E-02 4.23E-02 2.33E-02 1.02E-02 3 7.92E-02 5.21E-02 2.63E-02 9.28E-03 2.14E-03 4 7.54E-02 4.47E-02 1.80E-02 4.63E-03 6.76E-04 8 6.91E-01 3.36E-02 9.95E-03 I .59E-03 1.12E-04 1.75 2.00 3.14E-02 2.11E-02 3.62E-03 9.83E-04 3.49E-04 3.85E-05 6.99E-05 4.78E-06 7.26E-06 1.12E-06 Table 4.4. Simulated BER values for simulation 2 after 10,000 and 100,000 blocks 1.E+00 1.E-01 "0 1.E-02 1.E-03 w 1.E-04 1.E-05 1.E-06 1.E-07 0 0.5 1.5 1 2 Eb/No Figure 4-4. Simulation 2 BER curves for Table 4.2 and Table 4.4 2.5 SIMULATION RESULTS 4.1.3 75 Simulation 3: Rate-2/4 Turbo code This simulation uses 1024 bits per block and generator polynomials a =35 and b = 238. The signal set is square 16-QAM. Information bit (u) Parity bit (pI) Parity bit (p2) Bit mapping per symbol u1 p U2 P2 (bo, b, , b2 , b3) = (up, p , u2 , p 2) Table 4.5. Simulation 3 puncturing and mapping scheme Eg/NO 2.0 2.5 3.0 3.5 4.0 4.5 5.0 1 9.00E-02 8.OOE-02 6.50E-02 4.00E-02 2.00E-02 6.00E-03 1.00E-03 2 9.00E-02 7.OOE-02 3.00E-02 9.00E-03 8.00E-04 3.OOE-05 1.00E-06 Iterations 3 9.OOE-02 6.OOE-02 2.50E-02 2.00E-03 5.OOE-05 6.00E-07 5.OOE-08 4 9.OOE-02 5.50E-02 2.20E-02 9.00E-04 2.00E-05 3.OOE-07 5.OOE-08 8 9.OOE-02 5.00E-02 1.1OE-02 3.00E-04 2.00E-06 9.00E-08 2.00E-08 Table 4.6. Approximate BER values from cited graphs for simulation 3 Turbo code EdNo 2.0 2.5 3.0 3.5 4.0 1 9.34E-02 7.49E-02 5.34E-02 3.16E-02 1.41E-02 2 8.96E-02 6.38E-02 3.1OE-02 6.84E-03 5.23E-04 Iterations 3 8.89E-02 5.93E-02 2.01E-02 1.77E-03 3.OOE-05 4 8.88E-02 5.67E-02 1.51E-02 7.55E-04 7.52E-06 8 8.86E-02 5.31E-02 1.00E-02 2.63E-04 2.25E-06 4.5 5.0 4.56E-03 1.07E-03 2.04E-05 9.77E-07 8.40E-07 1.27E-07 6.05E-07 7.81E-08 2.73E-07 4.88E-08 Table 4.7. Simulated BER values for simulation 3 after 10,000 and 100,000 blocks CHAPTER FOUR CHAPTER FOUR 76 76 1.E+00 1.E-01 1.E-02 1.E-03 w 1.E-04 1.E-05 1.E-06 1.E-07 1.E-08 1.5 2 2.5 3 3.5 4 4.5 5 5.5 Eb/No Figure 4-5. Simulation 3 BER curves for Table 4.6 and Table 4.7 Figure 4-5 shows that for BER above 1.E-07, simulated results match cited results relatively closely. For lower BERs, the tool may not have simulated enough blocks, and results do not match as well. 4.1.4 Simulation 4: Rate-2/4 Turbo code This simulation uses a block size of 4096 bits with the Turbo code parameters of simulation 3. The interleaver depth increases to 4096, which should improve the performance of the Turbo code. The lack of a BER value for Eb /No = 3.5 [dB] indicates that not enough blocks were simulated to generate error. SIMULATION RESULTS 77 Eb/No 2.5 3.0 3.5 1 7.60E-02 5.45E-02 3.20E-02 2 6.56E-02 3.08E-02 4.85E-03 Iterations 3 6.18E-02 8.52E-03 1.07E-05 4 6.01E-02 8.52E-03 1.07E-05 8 5.69E-02 1.50E-03 4.0 1.41E-02 1.59E-04 5.18E-07 5.86E-08 1.95E-08 Table 4.8. Simulated BER values for simulation 4 after 2,500 and 25,000 blocks 1.E+00 1.E-01 1.E-02 1.E-03 u1.E-04 In 1.E-05 1.E-06 1.E-07 1.E-08 2 2.5 3.5 3 4 4.5 Eb/No Figure 4-6. Simulation 4 BER curves for Table 4.6 and Table 4.8 Figure 4-6 shows that, as expected, using a longer interleaver improves the performance of the Turbo code. 78 CHAPTER FOUR 4.1.5 Simulation 5: Rate-2/4 Turbo code This simulation uses generator polynomials ao = 17, and b =15, with the Turbo code parameters of simulation 3. The block size is 1024 bits. Iterations E/No 2.0 2.5 3.0 3.5 4.0 1 8.62E-02 6.84E-02 4.95E-02 3.15E-02 1.68E-02 2 8.05E-02 5.68E-02 3.02E-02 9.84E-03 1.68E-03 3 7.96E-02 5.30E-02 2.18E-02 3.66E-03 2.26E-04 4 7.94E-02 5.13E-02 1.75E-02 1.88E-03 8.01E-05 8 7.93E-02 4.93E-02 1.29E-02 8.24E-04 1.91E-05 4.5 5.0 7.42E-03 2.56E-03 1.66E-04 1.14E-05 1.08E-05 6.48E-07 5.00E-06 3.91E-07 1.78E-06 1.95E-07 Table 4.9. Simulated BER values for simulation 5 after 10,000 and 100,000 blocks As with simulation 2, Figure 4-7 shows again that for the Turbo encoder structure from [15], using component codes with polynomials ao = 35 and bo =23 yields better BER performance than using codes with polynomials ao = 17, and bo = 158. SIMULATION RESULTS 1.E+00 79 1 1.E-01 1. E-02 N1. E-03 i i -- i %P w I % Ne % 1.E-04 1.E-05 xI - 1.E-06 1.E-07 1. E-08 1.5 2 2.5 3 3.5 4 4.5 5 Eb/No Figure 4-7. Simulation 5 BER curves for Table 4.6 and Table 4.9 5.5 CHAPTER FOUR 80 4.1.6 Simulation 6: Rate-3/4 Turbo code This simulation uses 6144 bits per block and generator polynomials a =35, and bo = 23,. The signal set is square 16-QAM. Information bit (u) Parity bit (p') Parity bit (p2) Bit mapping u1 u2 u3 u4 - P2 - - _ _ (bo ,b1 ,b2 ,b3 ) = (uI, u2 , u3 p 1p2) U5 U6 p25 (bo ,b1 ,b2 ,b3) = (u4 , u5 , u6 , p 5) Table 4.10. Simulation 6 puncturing and mapping scheme Iterations EdNo 4.5 5.0 5.5 6.0 1 6.00E-02 4.00E-02 2.50E-02 9.OOE-03 2 6.00E-02 3.50E-02 7.OOE-03 2.00E-04 3 6.00E-02 2.50E-02 9.OOE-04 9.OOE-07 4 6.OOE-02 2.00E-02 1.1 OE-04 9.50E-08 8 6.OOE-02 1.50E-02 1.1 0E-07 - Table 4.11. Approximate BER values from cited graphs for simulation 6 Turbo code EWNo 4.50 4.75 5.00 5.25 5.50 1 5.49E-02 4.74E-02 3.94E-02 3.09E-02 2.25E-02 2 5.23E-02 4.24E-02 3.03E-02 1.70E-03 6.34E-03 Iterations 3 5.19E-02 4.07E-02 2.49E-02 8.07E-03 8.46E-04 5.75 6.00 1.52E-02 9.08E-03 1.45E-03 1.89E-04 3.38E-05 8.40E-07 4 5.18E-02 3.98E-02 2.05E-02 3.34E-03 1.08E-04 8.40E-07 1.56E-07 8 5.18E-02 3.82E-02 1.19E-02 5.64E-04 3.9 1E-07 - Table 4.12. Simulated BER values for simulation 6 after 1,666 AND 16,666 blocks SIMULATION RESULTS 81 1.E+00 1.E-01 1.E-02 1.E-03 M 01- 1.E-04 1.E-05 1.E-06 1.E-07 1.E-08 4 4.5 5 5.5 6 6.5 Eb/No Figure 4-8. Simulation 6 BER curves for Table 4.11 and Table 4.12 Aside from the lower BERs, Figure 4-8 shows that simulated results match cited results relatively well. 4.1.7 Simulation 7: Rate-3/4 Turbo code This simulation uses 6144 bits per block and rate-3/4 convolutional encoders with polynomials ao =118, a0 =158, a =178, and bg =138. The rate-3/4 code is the one specified in [18] and is one of the encoders in Figure 3-3. The signal set is square 16QAM. CHAPTER FOUR 82 Information bits (u) Parity bit (p1 ) Parity bit (p2 ) Bit mapping u1 , u2 , u3 U4 , U5, U6 p 1 (bo ,b1 ,b2 ,b3) = (u I, , u) Ip1 1) P2 (bo ,b1 ,b2 ,b3 ) = (u4 , u5 , u6 , p 2) Table 4.13. Simulation 7 puncturing and mapping scheme Iterations Eb/No 4.5 5.0 5.5 6.0 1 5.42E-02 3.84E-02 2.19E-02 9.12E-03 2 5.07E-02 2.81E-02 6.31E-03 4.08E-04 3 4.99E-02 2.16E-02 1.14E-03 7.70E-05 4 4.96E-02 1.68E-02 2.79E-04 1.22E-04 8 4.94E-02 8.15E-03 3.91E-03 9.89E-04 Table 4.14. Simulated BER values for simulation 7 after 1,666 AND 16,666 blocks The results in Table 4.14 are puzzling. Using a rate-3/4 encoder and the puncturing pattern in 4.13, much fewer parity bits are punctured than in the scheme in Table 4.10. Intuitively, having more parity bits, and thus more information, should improve the performance of the Turbo c ode relative to simulation 6. However, as Table 4.14 shows, performance is actually much worse. Further, the BER does not monotonically decrease with increasing iterations, which is odd. SIMULATION RESULTS 4.1.8 83 Simulation 8: Rate-3/6 Turbo code This simulation uses 6144 bits per block and generator polynomials a = 35 and b = 23,. The signal set is square 64-QAM. Information bit (u) Parity bit (p) Parity bit (p2) Bit mapping u1 p1 u2 u3 u4 -p P22 - (bo ,b1 ,b2 ,b3 , b4, b5 ) = (u], u 2 , p i, u3 , P22, p 3) u5 U6 P5 -5 P2 P 26 (bo ,b1 ,b2 ,b3, b4 , b5 ) = (u 4 , u5 , p24, u 6 , p 5, P26) Table 4.15. Simulation 8 puncturing and mapping scheme Iterations EdNo 5.0 5.5 6.0 6.5 1 6.OOE-02 4.OOE-02 2.1OE-02 1.00E-02 2 6.OOE-02 2.1OE-02 3.OOE-03 1.00E-04 3 6.OOE-02 1.00E-02 1.1OE-04 6.OOE-07 4 6.OOE-02 5.50E-03 2.OOE-06 8 6.OOE-02 5.50E-04 3.OOE-07 - Table 4.16. Approximate BER values from cited graphs for simulation 8 Turbo code EWNo 5.00 5.25 5.50 5.75 6.00 6.25 6.50 1 6.51E-02 5.65E-02 4.75E-02 3.83E-02 2.94E-02 2.13E-02 1.44E-02 2 6.48E-02 4.09E-02 2.60E-02 1.30E-02 4.70E-03 1.18E-03 2.OOE-04 Iterations 3 5.08E-02 3.19E-02 1.27E-02 2.30E-03 1.85E-04 7.03E-06 1.95E-07 4 4.86E-02 2.52E-02 5.13E-03 2.75E-04 1.27E-06 8 4.46E-02 1.13E-02 3.59E-02 - Table 4.17. Simulated BER values for simulation 8 after 1,666 blocks 84 CHAPTER FOUR 1.E+00 _-_--- 1.E-01 -_- 1.E-02 -_ - - -- _- 1.E-03 - 1.E-04 1.E-05 - 1 .E-06 1.E-07 4.75 5 5.25 5.5 5.75 6 6.25 6.5 Eb/No Figure 4-9. Simulation 8 BER curves for Table 4.16 and Table 4.17 Figure 4-9 shows that cited results and simulated results match relatively well. 6.75 SIMULATION RESULTS 4.1.9 85 Simulation 9: Rate-4/6 Turbo code This simulation uses 4096 bits per block and generator polynomials ao = 35 and bo = 238. The signal set is square 64-QAM. Information bit (u) Parity bit (p) Parity bit (p2 Bit mapping u1 p_ u2 u3 U4 (bo ,b ,b2 ,b3 , b4 , b5 ) = (ul, u 2 , p 1, u3 , u 4 , p 3) Table 4.18. Simulation 9 puncturing and mapping scheme Eb/No 6.75 7.00 7.25 7.50 7.75 8.00 8.25 1 7.00E-02 6.00E-02 5.OOE-02 4.00E-02 3.00E-02 2.GOE-02 1.00E-02 2 6.00E-02 5.00E-02 3.50E-02 2.70E-02 1.00E-02 3.O0E-03 8.50E-04 Iterations 3 6.00E-02 4.00E-02 2.20E-02 9.OOE-03 2.00E-03 3.OE-04 2.50E-05 4 5.00E-02 4.00E-02 2.00E-02 5.00E-03 5.00E-04 4.OOE-05 1.1 OE-06 8 4.00E-02 3.00E-02 1.70E-02 1.70E-03 5.0E-05 2.00E-06 2.GOE-07 Table 4.19. Approximate BER values from cited graphs for simulation 9 Turbo code Eb/No 6.75 7.25 7.75 8.00 7.75 8.00 8.25 1 5.08E-02 4.46E-02 3.81E-02 3.13E-02 2.45E-02 1.83E-02 1.27E-02 2 4.61E-02 3.73E-02 2.72E-02 1.69E-02 8.26E-03 2.97E-03 7.55E-04 Iterations 3 4.50E-02 3.45E-02 2.15E-02 8.90E-03 2.04E-03 2.43E-04 2.0GE-05 4 4.46E-02 3.29E-02 1.75E-02 4.70E-03 4.77E-04 2.50E-05 1.89E-06 8 4.43E-02 3.0IE-02 1.11E-02 1.19E-03 1.60E-05 2.34E-06 5.86E-07 Table 4.20. Simulated BER values for simulation 9 after 2,500 blocks 86 CHAPTER FOUR CHAPTER FOUR 86 1.E+00 1.E-01 1.E-02 w U In 1.E-03 1.E-04 1.E-05 1.E-06 1.E-07 L+j 6.5 6.75 7 7.25 7.5 7.75 8 8.25 Eb/No Figure 4-10. Simulation 9 BER curves for Table 4.19 and Table 4.20 Figure 4-10 shows that simulated results and cited results match relatively well. 8.5 SIMULATION RESULTS 4.1.10 87 Simulation 10: Rate-4/6 Turbo code This simulation uses 4096 bits per block and a rate-2/3 convolutional encoder with polynomials a = 35, a," =278, and be = 238. The rate-2/3 code is the one specified in [18] and is one of the encoder in Figure 3-3. The signal set is square 64-QAM. Information bit (u) Parity bit (p') Parity bit (p2) Bit mapping u1, u2 pI1 u3 , u 4 P 22 (bo ,bI ,b2 ,b3, b4 , b5 ) = (uI, u2 , P 1, u3 , u4 , p22) Table 4.21. Simulation 10 puncturing and mapping scheme EdNo 6.75 7.25 7.75 8.00 8.25 1 5.17E-02 3.89E-02 2.48E-02 1.82E-02 1.21E-02 2 4.76E-02 2.79E-02 7.35E-03 2.34E-03 4.37E-04 Iterations 3 4.66E-02 2.14E-02 1.40E-03 1.19E-04 3.32E-06 4 4.63E-02 1.69E-02 2.77E-04 8.33E-06 1.95E-07 8 4.60E-02 9.90E-03 9.67E-06 2.64E-07 1.95E-07 Table 4.22. Simulated BER values for simulation 10 after 2,500 and 25,000 blocks CHAPTER FOUR 88 CHAPTER FOUR 88 1. E+00 71f§_______ _______ 1.E-01 - 1.E-02 - 2I~<~I7T~ 1 E-03 - Xi 7~kL cc Lu X~'. XK 1 \ xi i r M0 1.E-04 -~ 1.E-05 I -- V -- - - r i- 1. E-06 1.E-07 6.5 6.75 7 7.25 7.5 7.75 8 8.25 8.5 Eb/No Figure 4-11. Simulation 10 BER curves for Table 4.19 and Table 4.22 The rate-2/3 convolutional code allows more parity bits to be retained than the scheme in simulation 9, while maintaining an overall code rate of 4/6. Figure 4-11 shows that, as expected, puncturing fewer parity bits results in better performance. This result matches what is intuitively correct. SIMULATION RESULTS 4.2 89 Results for Triple Parallel Concatenated Turbo Code The encoder structure for the Turbo code in CF-072 is that of Figure 3-2. All three component RSCC encoders have rate-1/2 and generator polynomials ao =7 and b0 =5 The encoders are implemented using the structure in Figure 2-2. The simulations used the semi-random interleaver algorithm from [21], which is equivalent to the generatable interleaver used in CF-072 [17]. The simulations only use square QAM signal sets. 4.2.1 Simulation 11: Rate-1/2 Turbo code This simulation uses 15844 bits per block and square 4-QAM. simulation indicate that a BER on the order of 10 -7 is Cited results for this achievable at Eb INO =1.6 [dB] with four decoder iterations. Information bit (u) u1 Parity bit (p1) p1_ Parity bit (p) Parity bit (p ) Bit mapping u2 U3 2 -2 (bo , b1 ) = (ui ,p 1) p 33 (bo , b1 ) = (u 2 p2) (bo , bi) Table 4.23. Simulation 11 puncturing and mapping scheme = (u3, p 3 ) CHAPTER FOUR 90 Iteration Eb/No 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 1 1.20E-01 9.62E-02 7.12E-02 4.64E-02 2.54E-02 1.1 3E-02 4.OOE-03 1.17E-03 2 1.19E-01 9.35E-02 6.49E-02 3.51E-02 1.23E-02 2.60E-03 3.69E-04 3.22E-05 4 1.19E-01 9.34E-02 6.39E-02 3.09E-02 6.70E-03 5.76E-04 2.39E-05 1.36E-06 3 1.19E-01 9.34E-02 6.40E-02 3.20E-02 8.37E-03 1.03E-03 7.51E-05 2.72E-06 Table 4.24. Simulated BER values for simulation 11 after 650 blocks As the tables above show, simulated results do not achieve a BER of 10-7 at E, /No = 1.6 [dB]. In fact, simulated results for this Turbo code are much worse than what is cited. 1.E+00 1.E-01 X---- ifX. 1.E-02 M 1.E-03 1.E-04 1.E-05 1.E-06 0 1 2 3 Eb/No Figure 4-12. Simulation 11 BER curves for Table 4.24 4 5 SIMULATION RESULTS 4.2.2 91 Simulation 12: Rate-1/2 Turbo code In this simulation, the rate-1/2 convolutional encoder with generator polynomials ao = 7 and b = 5 is used in place of the one in simulation I from Section 4.1.1. The results of this simulation will provide insight into the performance of the CF-072 component code relative to the CF-037 component code. The block size is 6144 bits per block, and the signal set is 4-QAM. Iterations EJNo 0.5 1.0 1.5 2.0 2.5 3.0 3.5 1 1.02E-01 8.77E-02 7.28E-02 5.78E-02 4.38E-02 3.14E-02 2.11 E-02 2 8.59E-02 6.43E-02 4.23E-02 2.33E-02 1.02E-02 3.62E-03 9.83E-04 3 7.92E-02 5.21E-02 2.63E-02 9.28E-03 2.14E-03 3.49E-04 3.85E-05 4 7.54E-02 4.47E-02 1.80E-02 4.63E-03 6.76E-04 6.99E-05 4.78E-06 8 6.91E-01 3.36E-02 9.95E-03 1.59E-03 1.12E-04 7.26E-06 1.12E-06 Table 4.25. Simulated BER values for simulation 12 after 1,666 blocks CHAPTER FOUR CHAPTER FOUR 92 92 1.E+00 V Jfk - _________ 1~ -7 ________ 1.E-01 - cc ______________ ______________ ______________ - ______ ______ ______ 1%W- __________________ ________ ~1~~ I _____________ _________ ___ -----H- 1.E-02 - 1.E-03 1.E-04 I. 0 0.5 1 1.5 2 2.5 I 3 3.5 4 4.5 Eb/No Figure 4-13. Simulation 12 BER curves for Table 4.25 Figure 4-13 shows that the code with polynomials ao = 7 and bo = 5 performs much worse than simulation 1, even with a much longer interleaver length. Further, the curves do not exhibit sharply decreasing BERs. Thus, it is not surprising that simulation 11 did not perform as well as simulation 1, even though cited results indicate it should. 4.3 Summary of Results Simulations 1, 3, 6, 8, and 9, used the Turbo encoder structure from Figure 2-9 and the exact parameters described in [15]. These simulation results matched cited results relatively well. Simulations 2, 4, 5, 7, 10, used the same Turbo encoder structure but different parameters. Of these five simulations, all but simulation 4 tested the SIMULATION RESULTS 93 performance of different component codes and corresponding puncturing patterns. These results are summarized below in Table 4.26. simulation 1 2 structure Fig. 2-9 Fig. 2-9 3 4 Fig. 2-9 Fig. 2-9 5 Fig. 2-9 6 7 Fig. 2-9 Fig. 2-9 8 9 10 Fig. 2-9 Fig. 2-9 Fig. 2-9 11 12 Fig. 3-2 Fig. 2-9 details rate-1/2 turbo code used RN-027 RSCC and compared to simulation I rate-2/4 turbo code used longer interleaver and compared to simulation 3 used RN-027 RSCC and simulated results matcbed-BA-020RI results RN-027 RSCC performed worse than BA-020R1 matched BA-020R1 results longer interleaver improved BER performance RN-027 RSCC performed compared to simulation 3 worse than BA-02OR1I rate-3/4 turbo code used rate-3/4 RSCC and compared to simulation 6 rate-3/6 turbo code rate-4/6 turbo code used rate-2/3 RSCC and compared to simulation 9 compared to CF-072 results used CF-072 RSCC and Scompared to simulation I matched BA-020R1 results results do not match expectations and are odd matched BA-020R1 results matched BA-020R1 results less puncturing improved BER performance did not match citedrtesuls CF-072 RSCC performed worse than BA-020R1 Table 4.26. Summary of simulation results CHAPTER FIVE DISCUSSION The simulation tool was used to evaluate two Turbo code structures: the encoders in Figure 2-9 and Figure 3-2. Simulation results for those structures were compared to cited results in [15] and [17], respectively, and only the results in [15] were reproduced. The other ITU Turbo code proposals listed in section 3.1 were not detailed enough to simulate. Even so, the twelve simulations shown in Chapter 4 are amply sufficient to determine the reliability of the simulation tool. The following sections will discuss the reliability of the simulation tool and will also analyze its performance in terms of resource consumption. 5.1 Reliability of Simulation Tool The main purpose of the simulation tool is to measure the performance of different Turbo codes. The only way to simulate the true performance of a Turbo code using block size N - 95 - 96 CHAPTER FIVE is to send all 2N possible blocks through the code. However, any practical value of N would be too large to allow the simulation to finish. Thus, to simulate a BER of 10', a general rule is to send ceil(lOx+2 IN) blocks through the code. A BER produced using this rule can be considered a good estimate of the performance of the Turbo code. Simulations 1, 3, 6, 8, and 9, used exact Turbo code parameters from [15] and produced results that match cited performance curves relatively well. The cited results were taken from [15], and it was fortunate that the proposal was described in sufficient detail for simulation. The fact that these simulations were able to match cited results indicates that Turbo code encoding and decoding algorithms were implemented correctly in the tool. Simulations 4 and 10 provide further confidence that the tool is able to produce correct results. The parameter changes in both simulations were meant to improve BER performance over simulations 3 and 9, respectively, and results indicate that they did. Thus, the tool was also able to produce uncited results that match intuitive trends. 5.2 Unexpected Simulation Results Simulations 7 and 11 produced unexpected results. Simulation 7 used a component RSCC that had a higher rate than simulation 6, allowing for fewer parity bits to be punctured. Although the simulation 7 RSCC is different from the simulation 6 RSCC, we can still reasonably expect this change to improve BER performance over simulation 6. As results show, not only was performance in simulation 7 worse than in simulation 6, but the BER also increased at higher iterations. The BER increase at higher iterations is unlikely a result of overflow errors because program code exists to limit the values of DISCUSSION MAP decoding parameters. 97 It is possible that some property of the RSCC used in simulation 7 caused extrinsic information to behave in a manner that diverges towards greater errors. In contrast, a higher rate RSCC was used in simulation 10 to improve BER performance over simulation 9, and the results produced in that trial did not exhibit the abnormalities seen in simulation 7. Simulation 10 suggests that simulation tool algorithms are indeed correctly implemented. Simulation II results performed worse than cited results by more than 2 dB. Simulation 12 used the same component RSCCs as in simulation 11, but on the Figure 29 Turbo code structure. Results indicate that the RSCC used in simulation II yields performance that is much worse than either of the RSCCs used in simulations I or 2. Thus, the results generated by simulation 11 are not completely surprising. CF-072 [17] did not provide complete details on all Turbo code parameters, and it is possible that simulation I I was incorrectly configured. 5.3 Simulation Speed and Memory Usage The simulation tool used double precision floating point variables to hold all real values. Each double variable is 8 bytes. Table 5.1 below shows the approximate memory usage for each type of module. The variable blockSize refers to the number of information bits per block. When possible, variables from a module definition are used in the memory usage equation. CHAPTER FIVE 98 Module / data structure one linked list Memory usage (bytes) bit source LL( # of elements, valueLength)= 16 + (# of elements) - ( 8 + 17 valueLength) For a rate-k/(k+1) RSCC: LL( (blockSize 1k), k) interleaver 4 - ( (blockSize/k) + 2 ) + LL((blockSize/k), k) convolutional encoder Rate-k/(k+r) code, 2" states 16 + 5 -2v + 2 v+k+4 + LL(blockSize, r) puncturer 12 + numInputs - (punctureLength + 4) + LL( # of elements, 1) symbol mapper 40 + (M + 2) -(log 2 M) channel 12 + LL(# of symbols, 1) demodulator 2 -LL(blockSize, 1) unpuncturer MAP decoder 16 + numOutputs - punctureLength + LL( # of elements, 1) numOutputs 32 + k -2k + 8 - blockSize -2 k*v + 2 -LL(blockSize, 2 k) main program 120 + (# of iterations)- LL(blockSize, 1) + 8 -M + LL(# of symbols, 1) Table 5.1. Simulation tool memory usage Table 5.2 below shows the approximate simulation speed when using a rate-1/2 16-state RSCC, 1024 bits per block, and 8 decoder iterations. System Dell Optiplex GX150, P111-1GHz, 512K memory Dell Optiplex GX100, PIII-800MHz, 256K memory SUN Sunblade 100 workstation SUN Ultra 5 workstation Table 5.2. Simulation tool speed blocks / min 204 143 74 60 CHAPTER SIX CONCLUSION In conclusion, simulation results indicate that the modular simulation tool described in this thesis can be used with confidence to approximate the performance of Turbo codes proposed for ADSL. The simulation tool modules are designed to accommodate a wide range of parameters and should not need to be modified to simulate different Turbo codes. However, it is likely that new initialization routines need to be written to correctly configure modules for new Turbo code structures. The computational power available in current computing systems allows the tool to simulate BERs on the order of 10-7 in a reasonable amount of time. This BER is sufficient for ADSL applications. Using the tool to simulate a BER on the order of 10-8 or smaller will require a much longer amount of time. The main accomplishment of this thesis is the design of a flexible, modular framework for simulating Turbo codes. The initialization and execution functions listed -99 - 100 CHAPTER SIX in Table 3.1 and shown in Appendix C were written to work within this framework. They comprise only a small portion of possible Turbo code parameters. Barbelescu describes a "simile" interleave [13] that terminates both component RSCC encoders, which should improve the performance of the MAP decoder as compared to when one encoder is left unterminated. The use of non-square-QAM constellations is discussed in [8] and [16]. Le Goff shows in [8] that certain non-square-QAM constellations yield better performance. A feature that has not been implemented is the Turbo Trellis-coded modulation (TTCM) described in [12]. Various contributions have actually proposed TTCM [18] [19] [20], although details in these proposals are vague. While the parameter options and systems mentioned above are currently unavailable in the simulation tool, they are supported by the comprehensive module definitions shown in Chapter 3. The careful design described in Chapter 3 allows these new features to be implemented without needing to alter module definitions. Lastly, although the intent of this thesis is not to compare Turbo codes, simulation results do indicate that of the different rate-1/2 RSCCs recommended in [14], [1.7], and [20], the code from [14] with polynomials performance. ao = 35, and b = 238 yields the best APPENDIX A Main Simulation Code turbo sim-std.c turbo_sim-std.c file: This is the main program file that simulates turbo codes. #include #include #include #include #include #include #include #include #include "linkedlist.h" "source.h" #include #include #include #include #include <math.h> <time.h> <stdio.h> <string.h> <stdlib.h> int "interleave.h" "conv.h" "puncture.h" "mapper.h" "channel.h" "demod.h" "decoder.h" main(int argc, char* argv[}) /* == /* DECLARE GENERAL VARIABLES general simulation variables */ FILE* fp; int sourceSeed; char filename[80]; double EbOverNo, start, finish, delta; int blockSize; long num blocks; int iter; long total-bits; long* total-errors; /* /* int int int terminateBits; untermBits; numSymbols; char char *patl, *pat2; *bitmap; misc variables */ timet starttime, curr time; float tl, t2, t3; int hrs, mins, secs; int i; int int long long char double char char char k; selection; block; blocks-left, blocks-done; error; temp; tempname[80]; */ terminated; notTerminated; == */ file to write results */ seed for random # gen */ store file name */ Eb/No simulation range */ # of bits per block */ # of blocks per Eb/No /* # of decoder iterations */ total bits per Eb/No total decode errors per Eb/No */ # of bits to terminate trellis */ */ blockSize - terminateBits # of symbols sent thru channel */ */ /* puncture pattern */ /* symbol bit mapping keeps track of time scanf variables printf: time variables for loop index selection variables scanf: code structure for loop index keeps track of progress error indicator temporary variable DECLARE SIMULATION MODULES 101 - */ */ */ */ */ */ */ */ */ */ MAIN SIMULATION CODE 102 */ simulation modules * * /* bit linked lists linkList linkList linkList* */ origBitsLL; uncodedBitsLL; decodedBitsLL; /* systematic buffer /* uncoded bits buffer /* decoded bits buffer ENCODER modules interleaverVars encoderVars encoderVars punctureVars twoDsignalMapVars */ interleaverl; encoderl; encoder2; puncturer; mapper; /* /* /* /* /* channel, soft-data modules */ channel; channelVars demodulator; demodulatorvars unpuncturer; unPunctureVars DECODER modules MAPdecoderVars MAPdecoderVars interleaverVars interleaverVars interleaverVars interleaverVars MAP decoder 1 */ MAP decoder 2 */ extrins info interleaver */ extrin. info deinterleaver */ systematic bits interleaver*/ a posteriori prob deinterleaver */ == INIT GENERAL VARIABLES 1; - terminated /* AWGN channel /* demodulator /* unpuncture LLR values MAPl; MAP2; extrinsicinterleaver; extrinsic deinterleaver; Sinterleaver; Dprobdeinterleaver; -= interleaver encoder 1 encoder 2 puncturer signal mapper notTerminated = 0; = 171; sourceSeed = 0; blocks done == INITIALIZE SIMULATION MODULES -= \n printf("= TURBO-CODE SIMULATI ON printf("= ====\n") printf(" printf("\n"); printf("\n"); ------------------ \n"); printf(",-------------------------------------printf("\n"); \n"); s printf(" I >i \n"); >------------->.---------printf("I g m \n"); printf(" I n a \n"); information bits -- > encoderl printf("I a p \n'); printf(" I --. -> encoder2 p \n"); interleaverl printf('I |1 \n") ; printf(" I |e r \n'); puncture printf("I printf(" \n"); printf('---------------------------------------------------------printf("\n"); /* */ output file name printf(",-------------------------------------printf("I Enter output file name\n"); printf("I >> (fp == ------------------ \n"); "); scanf("%s", filename); printf('-------------------------------------fp = fopen(filename, "a"); if \n"); ------------------ \n"); NULL) printf("\n\nError opening file %s, program terminating\n", filename); else fclose(fp); /* encoderl, encoder2, puncturer, mapper choices for modules: printf("\n\n"); printf(",--------------printf("\n"); printf(" I conv code printf("ll --------printf("' printf(" 1 rate-1/2 printf(" I printf("I -------------------------------------- \n"); symbol bit map ---------sp overall \n"); code rate\n"); --------- \n'); rate-1/2\n"); 10 spsp rate-2/4\n"); 010000 000010\n"); sssp rate-3/4\n"); 10 sssp rate-3/4\n"); parity puncture -----------10 01\n") \n"); printf("j printf(" printf("I printf("I printf(" I 2 printf("I \n"); printf("I 4 I */ rate-1/2 \n"); 3 rate-1/2 rate-3/4 01\n") turbo sim-std.c 01\n") printf("I printf("I \n"); printf("I printf("I 0 see more selections...\n"); \n"); printf("j--------------------------------------printf("1 Enter selection\n"); /* n") ; check for erroneous input */ error = 1; while (error) printf("I >> "); scanf("%d", &selection); if ((selection >= 0) && (selection <= 4) error = 0; else printf(" ** invalid selection **\n"); printf("----- if 0) (selection == printf("\n\n"); printf(",-----printf(" I c onv code printf(" J printf("I printf("|I 5 r ate-1/2 printf("| printf("I printf(" \n") r 6 printf("I printf("| \n") 7 r ate-2/3 [ printf("| --------------------- \n".); \n"); symbol - ------------ - --------- bit map code rate\n"); 10 01\n"); sspspp --------- \n"); rate-3/6\n") ; 1000 0010\n"); sspssp rate-4/6\n"); 10 01\n"); sspssp rate-4/6\n"); ate-1/2 printf(" I \n") r ate-1/2 8 printf("I printf("I parity puncture printf("| 1000100100 ssspsspp 0010010001\n"); rate-5/8\n"); \n") printf("| printf("I 0 s ee more selections...\n"); printf("I \n"); --------------------printf(" printf("I Enter selection\n"); error = 1 /* n") ; check for erroneous input */ while (error) >> printf("I "); scanf("%d", &selection); if ((selection == 0) 11 ((selection >=5) && (selection <= error = 0; else printf ("I ** invalid selection **\n"); }; printf("'---------------------- ----------------- 8))) --- \n"); if (selection == 0) printf("\n\n"); printf(",----------------------------------printf(" parity printf(" conv code puncture -------------------printf("| printf(" I 9 rate-3/4 10 printf("I 01\n"); printf("j --------------------- \n") ; symbol bit map \n"); code rate\n"); --------- --------- \n");rate-6/8\n"); ssspsssp \n"); printf("1 10 rate-1/2 00100000 sssspssssp printf("| 00000100\n"); printf(I \n"); printf("j 0 quit\n"); printf(I \n"); printf("I----------------------------------printf ("I Enter selection\n"); /* rate-8/10\n"); check for erroneous input */ error = 1; while (error) printf ("I >> ); scanf("%d", if &selection); ((selection == 0) 11 ((selection >=9) && (selection <= 10))) error = 0; else printf("| ** invalid selection **\n"); printf("'--------------------------------------------------------\n" printf("\n\n"); I; /* set initialization variables based on selection from menu above */ switch (selection) case 1: k = 1; pati = "10"; 103 MAIN SIMULATION CODE 104 pat2 = "01"; bitmap = "sp"; break; case 2: k = patl 1; = "10"; = "01"; bitmap = "spsp"; break; pat2 case 3: k = 1; patl = "010000"; pat2 = "000010"; bitmap = "sssp"; break; case 4: k = 3; patl = "10"; pat2 = "01"; bitmap = "sssp"; break; case 5: k = 1; patl = "10"; pat2 = "01"; bitmap = "sspspp"; break; case 6: k = 1; pati = "1000"; pat2 = "0010"; bitmap = "sspssp"; break; case 7: k = 2; patl = pat2 = "10"; "01"; bitmap = "sspssp"; break; case 8: k = 1; patl = "1000100100"; pat2 = "0010010001"; bitmap = "ssspsspp"; break; case 9: k = 3; patl = "10"; pat2 = "01"; bitmap = "ssspsssp"; break; case 10: k = 1; patl = "00100000"; pat2 = "00000100"; bitmap = "essspssssp"; break; }; /* switch */ Initialize encoders, puncture/unpuncture, signal mapper, interleavers */ if (selection != 0) printf("\n\n"); printf(",--------------------------------------------------------\n") encoderl, printf("I MODULES: selectEncoder(&encoderl, k); encoder2\n"); printf("'--------------------------------------------------------\n"); duplicateEncoder(&encoderl, &encoder2); we need v bits to terminate, but if code is rate-k/k+1, then v must be a multiple of k. so we round up v to the next higher multiple of k terminateBits = (int)ceil(encoderl.v / encoderl.k) * encoderl.k; twoPunctureInitVars(&puncturer, pati, s/ pat2); unPunctureInitVars(&unpuncturer, &puncturer); bitMapInitVars(&mapper.bitMapper, bitmap); printf("\n\n"); printf( ",--------------------------------------------------------\n") information bits\n"); printf("I MODULE: selectBlockSize(&blockSize, encoderi.k, mapper.bitMapper.numSys); printf("------------------------------------ /* select blockSize -------------------- \n"); /* initialize untermBits s/ untermBits = blockSize - terminateBits; numSymbols = (int) (blockSize / mapper.bitMapper.numSys); printf("\n\n"); printf(",--------------------------------------------------------\n"); /* interleaverl\n"); printf("| MODULE: if encoder is rate k/(k+l) where k > 1, then we need to interleave groups of k bits together. Thus, there will be only (blockSize / k) s/ elements in the linked list to interleave selectInterleaver(&interleaverl, (int) (blockSize / encoderl.k)); */ turbosim-std.c printf("'-- -------------- \n".); uncomment the code below to write an interleave pattern to file for retreival later. Also uncomment tempname[] in variable section above */ printf("write pattern to file (y/n)? scanf("%s", temp name); if (tempname[O] == 'y') printf("enter output printf("--> "); file name\n"); scanf ("%s", tempname); fp = fopen(tempname, "w"); outputPattern(&interleaverl, fclose(fp); llistInitVars(&origBitsLL, fp) encoderl.k) /* initialize info bits llistInitVars(&uncodedBitsLL, 1); /* initialize uncoded bits */ printf("\n\n"); printf(",---------------------------------- --------------------- printf("I MODULE: \n"); 2-D signal mapper\n"); selectSignalMapper(&mapper); printf("'---------------------------------- /* */ /* initialize signal mapper */ --------------------- \n"); channelInitVars(&channel); /* initialize channel */ demodulatorInitVars (&demodulator); /* initialize demodulator */ initialize Eb/No simulation range */ printf("\n\n"); printf(",---------------------------------printf ("I printf("I ENTER:\n"); \n"); printf(" Eb/No to start simulation\n"); printf(I >> scanf("%f", -------------------- \n"-); "); &tl); printf("J Eb/No to end simulation\n"); printf("j >> "); scanf("%f", &t2); printf(" I Eb/No step size\n"); printf("I >> "); scanf("%f", &t3); printf("( \n"); start = (double)tl; finish = (double)t2; delta = (double)t3; printf ("I Number of blocks per Eb/No\n"); printf ("I >> ); scanf("%d", &num blocks); blocksleft = /* /* number of blocks per Eb/No (long) (floor((finish - start) / delta) + 1) * * numblocks; initialize decoder parameters and decoder variables */ printf(" 1 Number of decoder iterations per block\n") printf("l >> "); scanf ("%d", &iter); /* select # of decode iterations */ printf("'---------------------------------------------------------\n") deinterleaverInitVars(&extrinsicdeinterleaver, &interleaverl); deinterleaverInitVars(&Dprob_deinterleaver, &interleaverl); deinterleaverInitVars(&extrinsic interleaver, &extrinsic deinterleaver); deinterleaverInitVars(&Sinterleaver, &extrinsicdeinterleaver); MAPdecoderInitVars(&MAP1, MAPdecoderInitVars(&MAP2, totalerrors = &encoderl, interleaverl.blockSize, &encoder2, interleaverl.blockSize, (long*)calloc(iter, sizeof (long)); /* init total errors array */ decodedBitsLL = (linkList*)calloc(iter, sizeof(linkList)) for (i=O; i < iter; i++) llistInitVars(&decodedBitsLL[i], 1); == /* /* DEFINE MODULE CONNECTIONS ENCODER modules */ encoderl.input = interleaverl.input= encoder2.input puncturer.input[0]= puncturer.input[1]= mapper.inputU = mapper.inputS mapper.inputP - origBitsLL.output; origBitsLL.output; interleaverl.output; encoderl.output; encoder2.output; uncodedBitsLL.output; origBitsLL.output; puncturer.output; channel, demodulator, unpuncturer */ channel.input = mapper.output; demodulator.input = channel.output; == &mapper. symbolMapper) &mapper. symbolMapper); 105 MAIN SIMULATION CODE 106 unpuncturer.input = demodulator.outputP; /* DECODER modules */ = MAPl.inputA extrinsic_deinterleaver.output; MAPl.inputS = demodulator.outputS; MAPl.inputP = &unpuncturer.output[0]; Sinterleaver.input= demodulator.outputS; extrinsic interleaver.input = MAPi.output extrinsic; MAP2.inputA = extrinsic interleaver.output; MAP2.inputS = Sinterleaver.output; MAP2.inputP = &unpuncturer.output[l]; extrinsicdeinterleaver.input = MAP2.output extrinsic; Dprobdeinterleaver.input = MAP2.outputDprob; == BEGIN SIMULATION PROGRAM time(&starttime); for (EbOverNo = start; /* EbOverNo finish; EbOverNo += delta) { /* this is in dB */ initialize bit and error counts to zero */ for (i=0; i < iter; totalerrors[i] totalbits = 0; /* = == i++) = 0; calculate channel noise variance for current Eb/No and constellation C/ channel.n variance = mapper.symbolMapper.Eav / (2 * mapper.bitMapper.numSys printf("Eb/No = %f, n-variance = %f\n", EbOverNo, channel.n variance); /* * pow(10, (EbOverNo/10))); initialize random # generator with same seed for each Eb/No to use same bit sequences srand(sourceSeed); for (block = 0; block < num blocks; block++) C/ totalbits += blockSize; genBits(&origBitsLL, untermBits); /* generate one block of bits C/ encodeslock(&encoderl); /* encoder original block */ terminateEncoder(&encoderl); doInterleave(&interleaverl); /* do interleaving encodeBlock(&encoder2); /* encode interleaved block C/ punctureBlock(&puncturer); /* puncture parity bits genBits(&uncodedBitsLL, (mapper.bitMapper.numUncoded * numSymbols)); /* uncoded bits /* regroup the systematic bits for use by the signal mapper */ if (encoderl.k > 1) regroupKtol(origBitsLL.output, encoderl.k); mapBlock(&mapper); sendAWGNblock(&channel); demodulateTwoDBlock(&demodulator, unpunctureBlock(&unpuncturer); if (encoderl.k > 1) /* note: /* generate symbols /* send through AWGN channel C/ &mapper, channel.n_variance) ; /* get LLRs */ /* unpuncture parity LLRs */ for some reason, log(2)); does not give the right number, but bits = (int) (loglO((*decoder).numInputs) / loglo(2)); does. bits = (int) (log((*decoder).numInputs) / regroupltoK(demodulator.outputS, (int) (loglO(MAPl.numInputs)/loglO(2))); calcprobS(&MAPl); calcgamma(&MAPl); initprobA(&MAP1, 1); doInterleave(&Sinterleaver); calcgamma(&MAP2); for /* only need to interleave L(dk) once */ (i=G; i < iter; i++) initAB(&MAP1, terminated); doMAPdecoding(&MAPl); doInterleave(&extrinsicinterleaver); initAB(&MAP2, notTerminated); doMAPdecoding(&MAP2); doInterleave(&extrinsicdeinterleaver); doInterleave(&Dprobdeinterleaver); probDecision(Dprobdeinterleaver.output, &decodedBitsLL[i]); 1; regroup_Ktol (demodulator.outputS, (int) (loglO(MAPl.numInputs)/loglO(2))); else init_probA(&MAPl, 0); calcgammaRl2(&MAPl); doInterleave(&Sinterleaver); calcgammaRl2 (&MAP2); for (i=0; i < iter; i++) initAB(&MAPl, terminated); doFastRl2MAPdecoding(&MAPl); doInterleave(&extrinsicinterleave initAB(&MAP2, notTerminated); doFastRl2MAPdecoding(&MAP2); only need to interleave L(dk) once */ C/ turbosim-std.c 107 doInterleave(&extrinsicdeinterleaver); doInterleave(&Dprobdeinterleaver); signDecision(Dprob deinterleaver.output, &decodedBitsLL[i]); }; / calculate and display decoded bit errors for (i=0; i < iter; totalerrors[i] i++) += */ (long)floor(blockSize * showErrorPercentage(&origBitsLL, &decodedBitsLL[i])); printf("Eb/No [dB] = %f, block %d / %d\n", EbOverNo, (block+l), num blocks); for printf / /* (i=0; i < iter; printf ("iteration ("\n"); i++) %d: %e\n", (i+1), ((double) total errors[ii / totalbits)) regroup origBitsLL from 1 bit per element back to k bits per element */ if (encoderl.k > 1) regroupltoK(origBitsLL.output, encoderl.k); find simulation time remaining blocksdone++; blocksleft--; */ time(&curr time); elapsedtime / blocksdone = remaining time / blocksleft remainingtime = elapsedtime / blocksdone blocksleft /* */ temp = (double)(blocks left * difftime(currtime, start time)/3600 / blocksdone); /* hours remaining */ hrs = (int)floor(temp); mins = (int)floor((temp - floor(temp)) * 60); temp *= 60; /* number of minutes remaining */ secs = (int)ceil((temp - floor(temp)) * 60); printf(" }; /* total time remaining: %dh %dm %ds\n\n", hrs, mins, secs); /* for loop: blocks */ append results for completed Eb/No to the output file */ fp = fopen(filename,"a"); fprintf(fp, "sourceSeed=%d\n", sourceSeed); fprintf(fp, "%d-QAM\n", mapper.symbolMapper.M); showTwoDmapping(&mapper.symbolMapper, fp); fprintf(fp, "\nbit map: ); for (i=0; i < mapper.bitMapper.b; i++) fprintf(fp, "%c", mapper.bitMapper.pattern[i]); fprintf(fp, "\nEav = %f\n", mapper.symbolMapper.Eav); fprintf(fp, "Eb/No = %f, nvariance = %f\n", EbOverNo, channel.n variance); fprintf(fp, "\nd bits -- Error probability:\n", totalbits); for (i=0; i < iter; i++) fprintf(fp, "iteration %d: %e\n", (i+1), ((double)totalerrors[i] / totalbits)); fprintf(fp, "\n"); temp = (double) (difftime(curr time, start-time)/3600 / (blocks done/num blocks)); /* elapsed for this Eb/No " hrs = (int)floor(temp); mins = (int)floor((temp - floor(temp)) * 60); temp *= 60; /* number of minutes remaining */ secs = (int)ceil((temp - floor(temp)) * 60); printf("simulation time for %d blocks: %dh %dm %ds\n", num blocks, hrs,mins,secs); fprintf(fp, "simulation time for %d blocks: %dh %dm %ds\n\n\n", numblocks, hrs,mins,secs); fclose(fp); /* for loop: /* Eb/No range */ free all dynamically allocated variables */ deleteList(&origBitsLL); /* cannot use freeList because list not dynamically allocated */ freeTwoDsignalMapVars(&mapper); freeChannelvars(&channel); freeDemodulatorVars(&demodulator); freeEncoderVars(&encoderl); freelnterleaverVars(&interleaverl); freeEncoderVars(&encoder2); freePunctureVars(&puncturer); deleteList(&uncodedBitsLL); freeUnpunctureVars(&unpuncturer); */ /* causes problems, don't use yet */ freeMAPDecoderVars(&MAPl); freeMAPDecoderVars(&MAP2); freeInterleaverVars (&extrinsic_interleaver) freelnterleaverVars(&extrinsic deinterleaver); freeInterleaverVars(&Sinterleaver); freeInterleaverVars(&Dprob_deinterleaver); for (i=0; i < iter; i++) deleteList(&decodedBitsLL[i); free(decodedBitsLL); }; /* if selection != 0 */ }; /* error opening file */ return 0; /* main */ MAIN SIMULATION CODE 108 turbo sim-icoding.c file: turbo sim-icoding.c This is the main program file that simulates turbo codes proposed by ICoding. ICoding only proposes to use rate-l/2 constituent codes, and full coding of every bit. */ #include #include #include #include #include #include #include #include #include #include "StdAfx.h" "linkedlist.h" "source.h" "interleave.h" "conv.h" "puncture.h" "mapper.h" "channel.h" "demod.h" "decoder.h" #include #include #include #include #include <math.h> <time.h> <stdio.h> <string.h> <stdlib.h> int main(int argc, char* argv[]) == === /* == == == == == === == misc variables * timet starttime, currtime; float tl, t2, t3; int hrs, mins, secs; int i; int k; int selection; long block; long blocks_left, blocks_done; char error; double temp; char temp_name[80]; * char terminated; char notTerminated; == CREATE == * keeps track of time scanf variables printf: time variables for loop index selection variables scanf: code structure for loop index keeps track of progress error indicator temporary variable SIMULATION MODULES /* simulation modules /* bit linked lists linkList linkList* * origBitsLL; decodedBitsLL; ENCODER modules interleaverVars interleaverVars encoderVars encoderVars encoderVars punctureVars * interleaverl; interleaver2; encoderl; encoder2; encoder3; puncturer; /* == V file to write results V seed for random # gen * store file name * Eb/No simulation range * # of bits per block * # of blocks per Eb/No /* # of decoder iterations * total bits per Eb/No total decode errors per Eb/No * # of bits to terminate trellis * * blockSize - terminateBits # of symbols sent thru channel * * /* puncture pattern / /* symbol bit mapping int sourceSeed; char filename[80; double EbOverNo, start, finish, delta; int blockSize; long numblocks; int iter; long total_bits; long* total_errors; int terminateBits; int untermBits; int numSymbols; char *patl, *pat2, *pat3; char *bitmap; /* == general simulation variables */ FILE* fp; /* == DECLARE GENERAL VARIABLES == / /* /* systematic buffer decoded bits buffer /* /* /* /* /* interleaver 1 interleaver 2 encoder 1 encoder 2 encoder 3 /* puncturer V V V V V V V V turbosim-icoding.c /* signal mapper twoDsignalMapVars mapper; channel, soft-data modules */ channelVars channel; demodulatorVars /* AWGN channel * demodulator demodulator; unPunctureVars unpuncturer; DECODER modules MAPdecoderVars MAPdecoderVars MAPdecoderVars interleaverVars /* /* /* /* extrinsic-interleaverl; /* extrinsic-interleaver2; /* extrinsicdeinterleaverl; /* extrinsic deinterleaver2; /* Sinterleaverl; /* Sinterleaver2; /* Dprob deinterleaver; /* MAPl; MAP2; MAP3; interleaverVars interleaverVars interleaverVars interleaverVars interleaverVars interleaverVars */ '/ '/ unpuncture LLR values */ MAP decoder 1 */ MAP decoder 2 '/ MAP decoder 3 extrins info interleaver 1 */ extrins info interleaver 2 */ extrin. info deinterleaver 1*/ extrin. info deinterleaver 2*/ systematic bits interleaver 1*/ systematic bits interleaver 2*/ a posteriori prob deinterleaver INIT GENERAL VARIABLES terminated notTerminated 1; 0; sourceSeed blocksdone 171; 0; = printf(I" printf( printf(" printf("\n") printf("\n"); printf(",----------------printf (" \n") printf(" printf("| printf("I printf(" printf("|l printf(I" printf("i" printf(i)" == INITIALIZE SIMULATION MODULES = = \n") TURBO-CODE SIMULATION == === == a i .------- I > encoderl information bits I I \n"); \n"); g I \n"); n I .-> a m interleaverl encoder2 --. I interleaver2 --- > encoder3 --. I I printf(I" I printf(" printf("I\n"); "----------------printf( printf ("\n"); \n"); \n"); 1 a \n"); p \n"); p \n"); | e \n"); r \n"); puncture */ output file name printf(", ---------------------- ---------- \n"); printf(i" Enter output file na printf("| >> ); scanf("%s", filename); printf("'---------------------- fp = fopen(filename, "a"); if (fp == NULL) printf("\n\nError opening file %s, program terminating\n", filename); else fclose(fp); /* choices for modules: encoderl, encoder2, puncturer, mapper printf("\n\n"); printf(",---------------------- ----------------------------------- \n"); printf("I\n"); printf("I printf("I printf("I printf("I printf("I printf("I printf("l printf("I printf("I printf("I printf("I printf("I printf("I printf("I printf("! 1 conv code --------rate-1/2 p arity p uncture --- --------1 00 symbol bit map ---------- overall \n"); code rate\n"); --------- \n") ; sp rate-1/2\n"); 1 00 010\n") spsp rate-2/4\n"); 0 10000000 0 00010000\n"); 0 00000010\n"); sqsp rate-3/4\n"); 010\n"); 001\n"); \n"); 2 rate-1/2 001\n") \n"); 3 rate-1/2 \n"); printf(" o printf("I \n"); see more selecti ons ...\n"); printf(i"---------------------printf("I Enter selection\n"); 109 MAIN SIMULATION CODE 110 /* check for erroneous input */ error = 1; while (error) >> printf(" scanf("%d", &selection); if ((selection >= 0) && (selection <: 3)) error = 0; else printf("| ** invalid selection **\n"); }; ---------- \n"); printf("'------------------------------------if (selection == 0) ("\n\n"); printf printf(",--------------------------------parity printf("j puncture conv code printf(" I -------------------printf("I printf(" l \n"); printf("Ul 0 quit\n"); printf("| \n"); printf("j-------------------------------- ---------------------- \n"); symbol bit map ---------- \n"); code rate\n"); --------- \n"); printf("| Enter selection\n"); check for erroneous input */ error = 1; while (error) /* printf("I >> scanf("%d", &selection); if ((selection == 0) 11 ((selection >=0) && (selection <= 0))) error = 0; else printf(" ** invalid selection **\n"); 1; ----------- \n"); printf("'--------------------------------------- printf ("\n\n") }; switch (selection) case 1: k = 1; "100"; patl = pat2 pat3 = "010"; = "001"; bitmap break; case 2: = "sp"; k = 1; patl = "100"; pat2 = "010"; pat3 = "001"; bitmap = "spsp"; break; case 3: k = 1; patl = "010000000"; pat2 = "000010000"; pat3 = "000000010"; }; /* /* bitmap = "sssp"; break; switch */ Initialize encoders, puncture/unpuncture, signal mapper, interleavers */ if (selection != 0) printf("\n\n"); printf(",--------------------------------------------------------\n"); printf(" I MODULES: encoderl, encoder2, encoder3\n"); selectEncoder(&encoderl, k); -------------------- \n"); printf("-----------------------------------duplicateEncoder(&encoderl, duplicateEncoder(&encoderl, &encoder2); &encoder3); we need v bits to terminate, but if code is rate-k/k+l, then v must be a multiple of k. so we round up v to the next higher multiple of k terminateBits = (int)ceil(encoderl.v / encoderl.k) * encoderl.k; */ threePunctureInitVars(&puncturer, patl, pat2, pat3); unPunctureInitVars(&unpuncturer, &puncturer); bitMapInitVars(&mapper.bitMapper, bitmap); printf ("\n\n") ; printf(",---------------------------------------------------------\n"); printf("I MODULE: information bits\n"); /* select blockSize selectBlockSize(&blockSize, encoderl.k, mapper.bitMapper.numSys); printf("'---------------------------------------------------------\n"); untermBits = blockSize - terminateBits; /* initialize untermBits s/ numSymbols = (int) (blockSize / mapper-bitMapper.numSys); turbosim-icoding.c printf("\n\n"); printf(" --------------------------------------------------------printf("I MODULE: interleaverl\n"); if encoder is rate k/(k+) where k > 1, groups of k bits together. elements in the linked list \n"); then we need to interleave Thus, there will be only (blockSize / k) to interleave */ selectInterleaver(&interleaverl, printf(" --------------------- (int) (blockSize / encoderl.k) \n"); printf ("\n\n"); printf(",-----------------------------------------------------------\n"); printf("I MODULE: interleaver2\n"); /* if encoder is rate k/(k+l) where k > 1, then we need to interleave groups of k bits together. Thus, there will be only (blockSize / k) elements in the linked list to interleave */ selectInterleaver(&interleaver2, (int) (blockSize / encoderi.k) printf("'-----------------------------------------------------------\n"); uncomment the code retreival later. below to write an interleave pattern Also uncomment to file for tempname[] in variable section above * printf("write pattern to file (y/n)? scanf("%s", temp name); if (tempname[O] == 'y') printf ("enter output file name\n"); printf("--> "); scanf("%s", tempname); fp = fopen(tempname, outputPattern(&interleaverl, "w"); fp); fclose(fp); */ llistInitVars(&origBitsLL, encoderl.k); /* initialize info bits printf("\n\n"); printf(",---------------------------------------------------printf("I MODULE: 2-D signal mapper\n"); / \n") selectSignalMapper(&mapper); /* initialize signal mapper */ channelInitVars(&channel); /* initialize channel demodulatorInitVars(&demodulator); /* initialize demodulator printf("'--------------------------------------------------------\n"); /* initialize Eb/No simulation range printf("\n\n"); printf(", printf("I printf("I * / / --------------------------------------------------------ENTER:\n"); \n"); \n"); printf ("I Eb/No to start simulation\n"); printf("I >> "); scanf("%f", &tl); printf("I Eb/No to end simulation\n"); printf("[ >> ); scanf("%f", &t2); printf("I Eb/No step size\n"); >> "); printf("I scanf("%f", &t3); printf("I \n"); start = (double)tl; finish = (double)t2; delta = (double)t3; printf("I Number of blocks per Eb/No\n"); printf("j >> scanf("%d", ); &numhblocks); /* number of blocks per Eb/No blocksleft = (long) (floor((finish - start) / delta) + 1) /* * * numblocks; initialize decoder parameters and decoder variables */ printf ("I Number of decoder iterations per block\n") printf("j >> scanf("%d", ); &iter); /* printf("----------------------------------- select # of decode iterations */ --------------------- deinterleaverInitVars(&extrinsic deinterleaverl, &interleaverl); deinterleaverInitVars(&extrinsicdeinterleaver2, &interleaver2); deinterleaverInitVars(&Dprobdeinterleaver, &interleaver2); deinterleaverInitVars(&extrinsic interleaverl, &extrinsicdeinterleaverl); deinterleaverInitVars(&extrinsicinterleaver2, &extrinsicdeinterleaver2); deinterleaverInitVars(&Sinterleaverl, &extrinsic_deinterleaverl); deinterleaverInitVars(&Sinterleaver2, &extrinsicdeinterleaver2); MAPdecoderInitVars(&MAP2, MAPdecoderInitVars(&MAP2, MAPdecoderInitVars(&MAP3, &encoderl, interleaverl.blockSize, &encoder2, interleaverl.blockSize, &encoder3, interleaverl.blockSize, total-errors = (long*)calloc(iter, sizeof(long)); decodedBitsLL for = (linkList*)calloc(iter, (i=l; i < iter; i++) llistInitVars(&decodedBitsLL[i}, 1); /* &mapper.symbolMapper); &mapper.symbolMapper); &mapper.symbolMapper); init total-errors array */ sizeof(linkList)); 111 MAIN SIMULATION CODE 112 = *--- --- /* /* == DEFINE MODULE CONNECTIONS REGULAR TURBO CODE ENCODER modules */ encoderl.input = interleaveri.input= encoder2.input = interleaver2.input= encoder3.input = puncturer.input[0] = puncturer.input[11]= puncturer.input[2]= mapper.inputS = mapper.inputP = origBitsLL.output; origBitsLL.output; interleaverl.output; origBitsLL.output; interleaver2.output; encoderl.output; encoder2.output; encoder3.output; origBitsLL.output; puncturer.output; channel, demodulator, unpuncturer */ channel.input = mapper.output; demodulator.input = channel.output; unpuncturer.input = demodulator.outputP; /* DECODER modules */ = extrinsic deinterleaver2.output; MAPl.inputA MAPl.inputS = demodulator.outputS; = &unpuncturer.output[0]; MAPl.inputP Sinterleaverl.input= demodulator.outputS; = MAPI.output extrinsic; extrinsicinterleaverl.input = extrinsic interleaverl.output; MAP2.inputA MAP2.inputS = Sinterleaverl.output; = &unpuncturer.output[l]; MAP2.inputP Sinterleaver2.input= demodulator.outputS; extrinsic deinterleaverl.input = MAP2.output extrinsic; = extrinsic-deinterleaverl.output; extrinsic interleaver2.input = extrinsic-interleaver2output; = Sinterleaver2.output; = &unpuncturer.output[2]; MAP3.inputA MAP3.inputS MAP3.inputP extrinsic deinterleaver2.input = MAP3.output extrinsic; Dprob_deinterleaver.input = MAP3.output Dprob; /* == BEGIN SIMULATION PROGRAM == time(&start time); for (EbOverNo = start; EbOverNo <= finish; EbOverNo += delta) /* { /* this is in dB */ initialize bit and error counts to zero '/ for (i=O; i < iter; i++) totalerrors[i] = 0; totalbits = 0; calculate channel noise variance for current Eb/No and constellation */ channel.n variance = mapper.symbolMapper.Eav / (2 * mapper.bitMapper.numSys * pow(10, (EbOverNo/10))); printf("Eb/No = %f, n-variance = %f\n", EbOverNo, channel.n_variance); /* /* initialize random # generator with same seed for each Eb/No to use same bit sequences */ srand(sourceSeed); for (block = 0; block < numblocks; block++) total bits += blockSize; genBits(&origBitsLL, untermBits); encodeBlock(&encoderl); terminateEncoder(&encoderl); doInterleave(&interleaverl); encodeBlock(&encoder2); doInterleave(&interleaver2); encodeBlock(&encoder3); punctureBlock(&puncturer); mapBlock(&mapper); sendAWGNblock(&channel); demodulateTwoDBlock(&demodulator, unpunctureBlock(&unpuncturer); /* all constituent codes are rate-1/2, initprobA(&MAPl, 0); calc gammaR12(&MAP1); doInterleave(&Sinterleaverl); calc gammaR12(&MAP2); doInterleave(&Sinterleaver2); calc gammaR12(&MAP3); for (i=O; i < iter; i++) { /* /* generate one block of bits encoder original block */ */ /* /* /* /* /* do interleaving encode interleaved block do interleaving encode interleaved block puncture parity bits */ */ */ */ */ /* generate symbols */ /* send through AWGN channel &mapper, channel.n variance); /* get LLRs */ /* unpuncture parity LLRs so use fast-ratel2 decoding functions */ /* only need to interleave L(dk) once */ /* only need to interleave L(dk) once */ initAB(&MAP1, terminated); doFastRl2MAPdecoding(&MAPl); doInterleave(&extrinsicinterleaverl); initAB(&MAP2, notTerminated); doFastRl2MAPdecoding(&MAP2); doInterleave(&extrinsic deinterleaverl); doInterleave(&extrinsic interleaver2); turbosim-icoding.c initAB(&MAP3, 113 notTerminated); doFastRl2MAPdecoding(&MAP3); doInterleave(&extrinsic_deinterleaver2); doInterleave(&Dprob_deinterleaver); signDecision(Dprob_deinterleaver.output, &decodedBitsLL[i]); }; /* calculate and display decoded bit errors for (i=0; i < iter; i++) totalerrorsi] += (long)floor(blockSize * showErrorPercentage(&origBitsLL, &decodedBitsLL[i])); printf("Eb/No [dB] = %f, block %d / %d\n", EbOverNo, (block+l), num blocks); for (i=0; i < iter; i++) printf ("iteration %d: %e\n", (i+l), ((double) totalerrors [ii / total-bits)); printf /* ("\n") ; find simulation time remaining blocks done++; blocksleft--; */ time(&curr time); /* elapsedtime / blocks-done = remainingtime / blocksleft remainingtime = elapsedtime / blocksdone blocks-left */ temp = (double) (blocksleft * difftime(currtime, starttime)/3600 / blocksdone); /* hours remaining */ hrs = mins (int)floor(temp); = (int)floor((temp - floor(temp)) printf(" }; /* * 60); 60; /* number of minutes remaining */ (int)ceil((temp - floor(temp)) * 60); temp secs total time remaining: %dh %dm %ds\n\n", hrs, mins, secs); for loop: blocks */ append results for completed Eb/No to the output file '/ fp = fopen(filename, a"); /* fprintf(fp, "sourceSeed=%d\n", sourceSeed); fprintf(fp, "%d-QAM\n", mapper.symbolMapper.M); showTwoDmapping(&mapper.symbolMapper, fp); fprintf(fp, "\nbit map: "); for (i=0; i < mapper.bitMapper.b; i++) fprintf(fp, "%c", mapper.bitMapper.pattern[i]); fprintf(fp, "\nEav = %f\n", mapper.symbolMapper.Eav); fprintf(fp, "Eb/No = %af, n variance = %f\n", EbOverNo, channel.nvariance); fprintf(fp, "\n%d bits -- Error probability:\n", total-bits); for (i=0; i < iter; i++) fprintf(fp, "iteration %d: %e\n", (i+l), ((double) totalerrors [i] / total-bits)); fprintf(fp, "\n"); temp = (double) (difftime(curr time, starttime)/3600 / (blocksdone/num blocks)); /* elapsed for this Eb/No */ hrs = (int)floor(temp); mins = (int)floor((temp - floor(temp)) * 60); temp *= 60; /* number of minutes remaining */ secs = (int)ceil((temp - floor(temp)) * 60); printf("simulation time for %d blocks: %dh %dm %ds\n", num blocks, hrs,mins,secs); fprintf(fp, "simulation time for %d blocks: %dh %dm %ds\n\n\n", num blocks, hrs,mins,secs); fclose(fp); /* /* for loop: Eb/No range */ free all dynamically allocated variables */ deleteList(&origBitsLL); /* cannot use freeList because list not dynamically allocated */ freeTwoDsignalMapVars(&mapper); freeChannelVars(&channel); freeDemodulatorVars(&demodulator); freeEncoderVars(&encoderl); freeInterleaverVars(&interleaverl); freeInterleaverVars(&interleaver2); freeEncoderVars(&encoder2); freePunctureVars(&puncturer); freeUnpunctureVars(&unpuncturer); */ * causes problems, don't use yet */ /* freeMAPDecoderVars(&MAP1); freeMAPDecoderVars(&MAP2); freeMAPDecoderVars(&MAP3); freeInterleaverVars(&extrinsicinterleaverl); freeInterleaverVars(&extrinsic deinterleaveri); freeInterleaverVars(&Sinterleaverl); freeInterleaverVars(&extrinsic_interleaver2); freeInterleaverVars(&extrinsicdeinterleaver2) freeInterleaverVars(&Sinterleaver2); freeInterleaverVars(&Dprob_deinterleaver); for (i=0; i < iter; i++) deleteList(&decodedBitsLL[i}); free(decodedBitsLL); }; }; /* /* if selection != 0 */ error opening file */ return 0; /* main */ MAIN SIMULATION CODE 114 turbosim-uncoded.c file: uncodedsim.c This is the main program file that simulates uncoded QAM performance. */ #include "linkedlist.h" "source.h" #include "interleave.h" #include "conv.h" #include "puncture.h" #include "mapper.h" #include "channel.h" #include "demod.h" #include "decoder.h" #include #include #include #include #include #include int <math.h> <time.h> <stdio.h> <string.h> <stdlib.h> main(int argc, char* argv[]) GENERAL VARIABLE DECLARATIONS general simulation variables */ int sourceSeed; FILE* fp; char filename[80]; double EbOverNo, start, finish, delta; int blockSize; long num blocks; int long iter; totalbits; long* totalerrors; terminateBits; untermBits; int int int numSymbols; misc variables int int long hrs, mins, secs; i; block; float long char double tl, t3; blocks-left, blocks-done; error; temp; == /* simulation modules /* bit linked lists linkList /* /* */ seed for random # gen */ file to write results store file name Eb/No simulation range */ # of bits per block */ # of blocks per Eb/No */ /* # of decoder iterations total bits per Eb/No */ total decode errors per Eb/No */ # of bits to terminate trellis */ */ blockSize - terminateBits # of symbols sent thru channel */ */ timet starttime, currtime; t2, /* /* /* /* /* keeps track of time printf: time variables for loop index for loop index */ */ */ scanf variables keeps track of progress error indicator temporary variable */ */ */ CREATE SIMULATION MODULES */ origBitsLL; channel, soft-data modules '/ channelVars channel; demodulatorVars demodulator; /* systematic buffer /* signal mapper /* AWGN channel /* demodulator INITIALIZE SIMULATION MODULES f (" == */ ENCODER modules */ twoDsignalMapVars mapper; print == == pri==========\n "); */ */ turbo sim-uncoded.c printf(" = -== == printf (" printf("\n"); printf ("\n") ; printf(",------------------printf("I\n"); printf(" UNCO DE SIMULATION =-= == ------------------ \n"); information bits -- > signal mapper --> channel\n"); printf)(I \n"); ------------------printf(" printf ("\n") printf ("\n") /* output file name */ printf(",------------------ --------------------------------------printf("| Enter output fi le name\n"); printf("I >> "); scanf("%s", filename); \n"); printf('------------------ --------------------------------------- \n"); fp = fopen(filename, "a"); if (fp == NULL) printf("\n\nError opening file %s, program terminating\n", else filename); fclose(fp); * --------- UNCODED SIMULATION ----------------------------- printf("\n\n"); printf(" -------------------------------------------------------printf("I MODULE: information bits\n"); selectBlockSize(&blockSize, 1, \n"); 1); /* '---------------------------------- printf( select blockSize ---------------------- \n"); printt("\n\n"); printf(",--------------------------------------------------------\n"); printf("I MODULE: bit mapper\n"); selectBitMapper(&mapper.bitMapper); printf("--------------------------------------numSymbols = (int) (blockSize ----------------- llistInitvars(&origBitsLL, 1); /* terminateBits = 0; untermBits = blockSize /* initialize untermBits */ terminateBits; - printf("\n\n"); printf(",--------------------------------printf("F MODULE: initialize info bits */ .---------------------\n"); 2-D signal mapper\n"); selectSignalMapper(&mapper); printf("--------------------------------- /* \n"); / mapper.bitMapper.numSys); /* initialize signal mapper */ ----------------------\n"); channelInitVars(&channel); /* initialize channel */ demodulatorInitVars(&demodulator); /* initialize demodulator */ initialize Eb/No simulation range */ printf("\n\n") ; printf(",--------------------------------printfQ(I --------------------- \n"); ENTER:\n"); printf("| \n"); printf("|l Eb/No to start simulation\n"); printf("I >> "); scanf("%f", &tl); printf("I Eb/No to end simulation\n"); printf("I >> "); scanf("%f", &t2); printf("I' Eb/No step size\n"); printf("I >> "); scanf("%f", &t3); printf(" \n"); start = (double)tl; finish = (double)t2; delta = (double)t3; printf("I Number of blocks per Eb/No\n"); printf("I >> "); scanf("%d", &num blocks); blocks-left I* = (long) (floor((finish - /* number of blocks per Eb/No start) / delta) + 1) */ * numblocks; initialize decoder parameters and decoder variables */ printf("'---------------------------------------------------------\n") blocks done = 0; blocksleft = (long) (floor((finish - start) / delta) + 1) iter = * numblocks; 1; totalerrors = (long*)calloc(iter, sizeof(long)); /* init totalerrors array */ /* - DEFINE MODULE CONNECTIONS == 115 115 MAIN SIMULATION CODE 116 / ----------- UNCODED mapper.inputS channel.input demodulator.input * ----------------------------SIMULATION = origBitsLL.output; = mapper.output; = channel.output; CHECK == SIMULATION PARAMETERS error = 0; if (error == 0) BEGIN == SIMULATION PROGRAM showTwoDmapping(&mapper.symbolMapper, NULL); time(&starttime); sourceSeed = 171; /* for (EbOverNo = start; EbOverNo <= finish; EbOverNo += delta) { /* this is in dB */ initialize bit and error counts to zero '/ /* for (i=0; i < iter; i++) totalerrors[i) = 0; 0; totalbits = calculate channel noise variance for current Eb/No and constellation */ channel.n-variance = mapper.symbolMapper.Eav / (2 * mapper.bitMapper.numSys printf("Eb/No = %f, n-variance = %f\n", EbOverNo, channel.n_variance); /* * pow(10, (EbOverNo/10))) initialize random # generator with same seed for each Eb/No to use same bit sequences */ srand(sourceSeed); for (block = 0; block < num_blocks; block++) /* totalbits += blockSize; / UNCODED SIMULATION --------------------------------------- *-- /* generate one block of bits */ genBits(&origBitsLL, blockSize); */ /* generate symbols mapBlock(&mapper); */ /* send through AWGN channel sendAWGNblock(&channel); decode uncoded(demodulator.outputS, &mapper, channel.output); */ find error rate totalerrors[0) += (long)floor(blockSize * showErrorPercentage(&origBitsLL, /* demodulator.outputS)); / find simulation time remaining blocks_done++; blocks_left--; */ time(&currtime); elapsedtime / blocks-done = remaining time / blocksleft blocksleft remaining time = elapsed time / blocks-done /* temp = (double) (blocksleft * difftime(currtime, start_time)/3600 / blocks_done); remaining /* hours / hrs = (int)floor(temp); mins = (int)floor((temp - floor(temp)) * 60); /* number of minutes remaining */ 60; temp secs = (int)ceil((temp - floor(temp)) * 60); 1; /* for loop: blocks printf("Eb/No [dB] printf("BER: %e\n", printf(" = / %f\n", EbOverNo); ((double)totalerrors[0} / total_bits)); total time remaining: %dh %dm %ds\n\n", hrs, mins, secs); printf("\n"); /* append results for completed Eb/No to the output file */ fp = fopen(filename,"a"); fprintf(fp, "sourceSeed=%d\n", sourceSeed); fprintf(fp, "%d-QAM\n", mapper.symbolMapper.M); showTwoDmapping(&mapper.symbolMapper, fp); fprintf(fp, "\nbit map: "); for (i=0; i < mapper.bitMapper.b; i++) fprintf(fp, "%c", mapper.bitMapper.pattern[il); fprintf(fp, "\nEav = %f\n", mapper.symbolMapper.Eav); fprintf(fp, "Eb/No = %f, nvariance = %f\n", EbOverNo, channel.nvariance); fprintf(fp, "\n%d bits -- Error probability:\n", total_bits); for (i=0; i < iter; i++) / totalbits)); fprintf(fp, "iteration %d: %e\n", (i+l) , ( (double)totalerrors[i fprintf(fp, "\n"); temp = (double) (difftime(currtime, start_time)/3600 / (blocksdone/num blocks)); /* elapsed for this Eb/No */ hrs = (int)floor(temp); mins = (int)floor((temp - floor(temp)) * 60); /* number of minutes remaining */ temp *= 60; secs = (int)ceil((temp - floor(temp)) * 60); printf("simulation time for %d blocks: %dh %dm %ds\n\n", num blocks, hrs,mins,secs); fprintf(fp, "simulation time for %d blocks: %dh %dm %ds\n\n\n", numblocks, hrs,mins,secs); turbosim-uncoded.c fclose(fp); /* }; /* /* if !error for loop: Eb/No /* cannot use freeList because list not dynamically allocated */ freeTwoDsignalMapVars (&mapper); freeChannelVars(&channel); freeDemodulatorVars(&demodulator); /* error opening file */ return 0; /* */ free all dynamically allocated variables */ deleteList(&origBitsLL); }; range */ main */ 117 APPENDIX B Sample Output for turbo_ sim-std.c Screen Output TURBO-CODE SIMULATION ------ -- > ---- ----- information bits -- > encoder interleaverl --- > s i g m n a > a p 1 p e r - -- > encoder2 --. puncture Enter output file name >> test.txt conv code parity puncture symbol bit map overall code rate 1 rate-1/2 10 01 sp rate-1/2 2 rate-1/2 10 01 spsp rate-2/4 3 rate-1/2 010000 000010 sssp rate-3/4 4 rate-3/4 10 01 sssp rate-3/4 0 see more selections... Enter selection >> 1 -119- 120 APPENDIX B encoderi, MODULES: 1 fbPoly = 23 fbPoly = 7 fbPoly = 15 2 3 encoder2 ffPoly = 35 ffPoly = 5 ffPoly = 17 select generator polynomials >> 1 information bits MODULE: ** must be divisible by 1 ** 1 2 3 4 5 1024 4096 5120 6144 15844 Select number of bits per block >> 1 MODULE: 1 2 3 interleaverl Random permutation Two-step semi-random algorithm (AT&T RN-029 standard) Read permutation pattern from file Select interleaver type > 2 ** 1024 elements ** S1, S2, seed 1 2 3 4 5 (16, (18, (23, (25, (26, 6, 12, 12, 15, 15, 151) 151) 151) 151) 151) Select semi-random interleaver parameters >> 2 generating interleave pattern ... MODULE: 1 2 3 2-D signal mapper normal bit labeling (00, 01, 10, 11) full gray labeling in each dimension (00, 01, concatenated gray labeling in each dimension Select labeling scheme 11, 10) SAMPLE OUTPUT >> 2 ENTER: Eb/No to start simulation >> 1 Eb/No to end simulation >> 1 Eb/No step size >> 1 Number of blocks per Eb/No >> 5 Number of decoder iterations per block >> 8 Eb/No = 1.000000, nvariance = 0.794328 Eb/No [dB] = 1.000000, block 1 / 5 iteration 1: 1.054688e-01 iteration 2: 9.960938e-02 iteration 3: 9.082031e-02 iteration 4: 9.082031e-02 iteration 5: 8.691406e-02 iteration 6: iteration 7: 8.789062e-02 8.496094e-02 iteration 8: 8.496094e-02 total time remaining: Eb/No [dB] iteration iteration iteration iteration iteration iteration iteration iteration = 1: 2: 3: 4: 5: 6: 7: 8: 1.000000, block 2 / 9.765625e-02 9.61914le-02 8.691406e-02 9.033203e-02 7.421875e-02 8.05664le-02 8.496094e-02 8.300781e-02 5 total time remaining: Eb/No [dB] iteration iteration iteration iteration iteration iteration iteration iteration = 1: 2: 3: 4: 5: 6: 7: 8: = 1: 2: 3: 4: 5: 6: 7: 8: Oh Om 2s 1.000000, block 3 / 5 7.910156e-02 6.477865e-02 5.794271e-02 6.022135e-02 4.947917e-02 5.371094e-02 5.664062e-02 5.533854e-02 total time remaining: Eb/No [dB] iteration iteration iteration iteration iteration iteration iteration iteration Oh Om 4s 1.000000, block 4 / 8.227539e-02 6.396484e-02 5.419922e-02 5.419922e-02 4.199219e-02 4.052734e-02 4.248047e-02 4.150391e-02 5 total time remaining: Eb/No [dBI = 1.000000, block 5 / iteration 1: 8.417969e-02 iteration 2: 6.386719e-02 Oh Om ls 5 Oh Om ls 121 122 iteration iteration iteration iteration iteration iteration APPENDIX B 3: 4: 5: 6: 7: 8: 5.019531e-02 4.375000e-02 3.359375e-02 3.242188e-02 3.398437e-02 3.320312e-02 total time remaining: Oh Om Os simulation time for 5 blocks: Oh Om 2s SAMPLE OUTPUT Output File test.txt sourceSeed=171 4-QAM [point, label] [(-1,-i), 00] [(-il), Oi] bit map: sp Eav = 2.00000 0 Eb/No = 1.000 000, nvariance = 0.794328 5120 bits iteration iteration iteration iteration iteration iteration iteration iteration Error probability: 8.417969e-02 6.386719e-02 5.01953le-02 4.375000e-02 3.359375e-02 3.242188e-02 7: 3.398437e-02 8: 3.320312e-02 1: 2: 3: 4: 5: 6: simulation time for 5 blocks: Oh om 2s [(1,-1), 10] [(1,1), 1l] 123 APPENDIX C Simulation Code general.h file: general.h This file contains definitions of general purpose functions used by different modules. /* basic functions */ double sqr(double a); double min(double a, double b); double max(double a, double b); /* convert between char[] and int * void intToCharArray(int s, char binary[], int int charArrayToInt(char binary], int v); v); - 125 - SIMULATION CODE 126 general.c /* general.c file: This source file contains code for general functions used by other routines. */ #include "general.h" /* general.h must come before math.h because max and min */ are already defined in math.h in some libraries. #include <math.h> /* function: sqr This function returns the square of an input double. Input double a Output (a * a) */ double sqr(double a) (a * a); return /* / sqr function: min This function returns the lesser of two doubles. double a double b lesser of a and b Input Output */ double min(double a, double b) return ((a <= b) ? a : b); } /* min function: max This function returns the greater of two doubles. double a double b greater of a and b Input Output */ double max(double a, double b) return /* max ((a >= b) ? a : b); * function: intToCharArray This function converts an integer value into binary. binaryt0] is the MSB of the integer, and binarytv-1] is the LSB of the integer. Input I Output : : int s char binary[] int v - the integer value - array to hold binary value - length of the binary array general.c I Modify : binary[] void intToCharArray(int s, char binary(], int v) = 6, v=3 binary[] is 1 1 0 indices are 0 1 2 / if s so index 0 is MSB and index 2 is LSB int i; for (i=0; i < v; i++) binary[v-1-i} = ((s /* intToCharArray >> i) & 1); / /* function: charArrayToInt This function converts a binary number into an integer. binary[0] is the MSB of the integer, and binary[v-1] is the LSB of the integer. char binary[] - array holding binary value int v - length of array the integer value Input Output Modify int charArrayToInt(char binary[}, /* if s = 6, v=3 binary[] is indices are int v) 1 1 0 0 1 2 so index 0 is MSB and index 2 is LSB */ sum = int i, for (i=0; i < v; i++) sum += (int)binary[i] 0; return sum; /* charArrayToInt / * (int)pow(2, (v-1-i)); 127 128 SIMULATION CODE Iinkedlist.h file: linkedList.h This header file contains variable and function definitions for a linked list. /* type definitions */ typedef struct twoDSignalPointStruct double i; double q; } twoDSignalPoint; typedef struct listElementStruct char *value; twoDSignalPoint *signal; struct listElementStruct *next, *prev; listElement; typedef struct linkListStruct int valueLength; listElement *head, *tail; struct linkListStruct *output; linkList; init/deallocation functions */ /* void llistInitVars(linkList* llist, int arrayLength); void freeList(linkList* list); /* adding elements */ void addElement(linkList* llist, listElement* newEl); /* add 1 element with specified bits */ void addLLbits(linkList* llist, char bits[]); /* add 1 element with specified signals */ twoDSignalPoint points[]); void addLLsignal(linkList* llist, char bits]], twoDSignalPoint points]]); /* add 1 element with specified data */ void addLLdata(linkList* llist, deleting elements */ /* void deleteFirstElement(linkList* llist); void deleteList(linkList* llist); /* get element pointer */ listElement* getElement(linkList* llist, int n); char* getValue(linkList* llist, int n); twoDSignalPoint* getSignal(linkList* llist, int n); /* show list contents */ void showLLbits(linkList* llist); void showLLsignals(linkList* llist); /* other functions */ char notEmpty(linkList* llist); int getNumElements(linkList* llist); void copyList(linkList* orig, linkList* copy); linkedlist.c Iinkedlist.c file: linkedlist.c This source file contains code for linked list functions. */ #include "linkedlist.h" #include <stdio.h> #include <stdlib.h> /* -------------- INITIALIZATION / DEALLOCATION FUNCTIONS --------- ----- /* function: llistInitVars This function initializes all variables in the input linked list. head and tail pointers are set to NULL, valueLength is set to the input variable arrayLength, and output points back to the list itself. I Input linkList *llist int arrayLength Output Modify llist struct elements void llistInitVars(linkList* llist, int arrayLength) (*llist).head = NULL; (*llist).tail = NULL; (*llist).valueLength = arrayLength; (*llist).output = llist; }; / llistInitVars * function: freeList Frees all dynamically allocated variables in the input linked list. Input Output Modify linkList *list - linked list pointer list void freeList(linkList* list) deleteList(list); free(list); S/* freeList */ /* --------- ----- ELEMENTS ------------------ADDING function: addElement Add an element to the end of the linked list. Input *llist - linked list pointer linkList listElement *newEl - new element pointer Output Modify : llist, newEl void addElement(linkList* (*newEl).next = NULL; llist, listElement* newEl) /* last element, set next to NULL V 129 SIMULATION CODE 130 if ((*l1ist).head == NULL) /* (*llist).head = newEl; (*llist).tail = newEl; this is first element in the list */ /* both head and tail point to this element no previous element (*newEl).prev =NULL; */ */ else at least one element in list already (*l(*list).tail).next = newEl; (*newEl).prev = (*llist).tail; (*l1ist).tail = newEl; */ /* new element now at end of linked list */ }; /* addElement */ /* function: addLLbits Add a new element with the specified bits to the end of the linked list. linkList* llist bits[] char Input Output Modify - linked list pointer array of bits llist */ void addLLbits(linkList* llist, char bits[]) { int i; */ /* allocate memory for new element listElement* newEl = (listElement*) calloc(1, sizeof(listElement)); /* allocate memory to hold bit values V (*newEl) value = (char*) calloc( (*llist) .valueLength, sizeof (char)); /* allocate memory (*newEl) signal = (twoDSignalPoint*) calloc( (*llist) .valueLength, sizeof (twoDSignalPoint)); to hold bit values */ for (i=O; i < (*llist).valueLength; i++) (*newEl).value(ii = bits[i]; '* copy bit values to new element */ addElement(llist, newEl); /* addLLbits */ /* function: addLLsignal Add a new element with the specified points to the end of the linked list. Input - linked list pointer llist linkList* twoDSignalPoint points)] - array of 2D points Output Modify void addLLsignal(linkList* llist, twoDSignalPoint points[]) int i; listElement* newEl = (listElement*) calloc(1, sizeof(listElement)); /* allocate memory for new element */ */ /* allocate memory to hold bit values (*newEl) value = (char*) calloc((*llist) .valueLength, sizeof (char)); /* allocate memory (*newEl) signal = (twoDSignalPoint*) calloc((*llist) .valueLength, sizeof(twoDSignalPoint)); to hold bit values s/ for (i=O; i < (*llist).valueLength; i++) (*newEl).signal[i] = points[i]; addElement(llist, newEl); /* addLLsignal */ function: addLLdata Add a new element with the specified points to the end of the linked list. Input linkList* char llist bits[] - linked list pointer - array of bits twoDSignalPoirnt points[] - array of 2D points Output Modify linkedlist.c */ void addLLdata(linkList* int llist, char valueBits)], 131 twoDSignalPoint points[]) i; listElement* newEl = (listElement*) calloc(1, sizeof(listElement)); /* allocate memory for new element / (*newEl) value = (char*) calloc( (*list) .valueLength, sizeof(char)); /* allocate memory to hold bit values / (*newEl) signal = (twoDSignalPoint*) calloc( (*llist) .valueLength, sizeof(twoDSignalPoint)); /* allocate memory to hold bit values / for (i=O; i < (*Ilist).valueLength; i++) /* (*newEl).value[i] = valueBits[i]; (*newEl).signal[i] = points[i]; copy bit values to new element V addElement(llist, newEl); /* /* addLLdata */ --------------- DELETING ELEMENTS -------- function: deleteFirstElement Delete first element of linked list. linkList* llist - linked list pointer Input Output Modify llist void deleteFirstElement(linkList* llist) listElement *temp; if ((*Ilist).head != NULL) temp = (*llist).head; (*llist).head = (* (*list).head).next; if /* set next element in list as first ((*llist).head 1= NULL) (*(*llist).head).prev = NULL; free((*temp).signal); free((*temp).value); /* free(temp); }; } /* deleteFirstElement delete previous first element * function: deleteList Delete all elements in the linked list. Input : linkList* llist - linked list pointer Output Modify : llist */ void deleteList(linkList* llist) while ((*llist).head != NULL) deleteFirstElement(llist); S/* /* deleteList */ --------------- GETTING ELEMENT POINTERS ------------------- /* function: getElement Get nth element from linked list. Input n=1,2,3,... : linkList* llist - linked list pointer int n - index of element to get Output : pointer to the list element to return Modify * / SIMULATION CODE 132 Illist, int n) listElement* getElement(linkList * n is from 1 to (# of element in list) int i; temp; listElement* if /* will */ hold pointer to list element to return */ (llist == NULL) temp = NULL; else temp = (*1list).head; for (i=O; i<(n-l); i++) temp = /* /* begin at first element of list go down list until we reach nth element (*temp).next; }; /* return temp; /* getElement return nth element */ */ /* function: getvalue Get the bit array of an element of the linked list. linkList* llist - linked list pointer int n - index of list element Output : bit array Modify Input */ char* getValue(linkList* llist, int n) return /* (*(getElement(llist,n))).value; getValue */ /* function: getSignal Get the signal point array of an element in the linked list. linkList* llist - linked list pointer int n - index of list element array of 2D points Input Output Modify twoDSignalPoint* getSignal(linkList* llist, int n) return (*getElement(llist,n)).signal; } /* /* getSignal ------------- */ DISPLAY LIST CONTENTS ------------- /* function: showLLbits Show the bits in every element of the linked list. linkList* llist - linked list pointer Input Output Modify void showLLbits(linkList* llist) int i; listElement* temp; temp = (*llist).head; while (temp !=NULL) /* traverse the list */ for (i=G; i < (*llist).valueLength; i++) */ */ linkedlist.c printf("%d", (*temp).value i]); temp = (*temp).next; }; printf("\n"); } showLLbits */ function: showLLsignals Show the signal points stored in every element of the list. linkList* llist - linked list Input Output Modify */ void showLLsignals(linkList* llist) int i; listElement* temp; temp = while (*llist).head; (temp != /* NULL) traverse the list */ for (i=O; i < (*llist).valueLength; i++) printf(" (%g,%g) ", (*temp).signal[i].i, (*temp).signal[i].q); temp = (*temp).next; }; printf("\n"); } /* showLLsignals --------------- /* */ OTHER FUNCTIONS ------------- /* function: notEmpty Test if there are any elements in the linked list linkList* llist - linked list pointer 0 if list is empty, else 1 Input Output Modify char notEmpty(linkList* llist) return ((*llist).head != NULL); } /* notEmpty */ function: getNumElements Get number of elements in the linked list. Input Output Modify int {t linkList* llist - linked list pointer : number of elements in list : getNumElements(linkList* llist) int count = 0; listElement* tempEl; tempEl = while (*llist).head; (tempEl != NULL) tempEl = (*tempEl).next; count++; 1; return count; /* getNumElements */ 133 SIMULATION CODE 134 function: copyList Copy all elements in one list to another. Input Output Modify linkList* orig - pointer to original linked list linkLIst* copy - pointer to linked list copy copy */ void copyList(linkList* orig, linkList* copy) int num, i; listElement* currEl; deleteList(copy); (*copy).valueLength = (*orig).valueLength; num = getNumElements(orig); currEl = getElement(orig, 1); for (i=0; i < num; i++) addLLdata(copy, currEl }; } /* copyList */ = (*currEl).value, (*currEl).signal); (*currEl).next; source.h source.h /* file: source.h This header file contains function definitions for generating bits. /* selection function */ void selectBlockSize(int *blockSize, int k, /* simulation command */ void genBits(linkList* llist, /* int numBits); debug/display function */ void showArrayBits(char bits[}, int size); int numSys); 135 SIMULATION CODE 136 source.c file: source.c This file contains code for modules that generate bits. #include "linkedlist.h" #include "source.h" #include <math.h> #include <stdlib.h> #include <stdio.h> SELECTION FUNCTION -------- --- function: selectBlockSize Selects the number of bits in an encode/decode block. The block size must be divisible by the number of bits, k, used as input to a rate-k/n convolutional code. Further, the block size must also be divisible by the number of systematic bits per symbol. int *blockSize - pointer to variable to hold block size - number of bits to input to a rate-k/n conv. encoder int k int numSys - number of systematic bits per symbol Input Output Modify : blockSize void selectBlockSize(int *blockSize, int k, int numSys) int selection; char error; int nl, n2, lcm; ni k; = n2 = numSys; /* assume k < numSys, so that k fits onto one symbol */ lcm = n2; while ((lcm % nl) 1cm += n2; != 0) printf("I---------------------------------------printf("I\n"); ** must be divisible by %d **\n", lcm); printf(" printf("I\n"); 1024\n"); 1 printf("I printf("I 2 4096\n"); 3 5120\n"); printf("I printf("I 4 6144\n"); 5 printf("I printf("\n"); 15488\n"); printf("I---------------------------------------printf("I Select number of bits per block\n"); error = 1; while (error) >> printf("j scanf("%d", &selection); if ((selection >= 1) & (selection <= 5)) error = 0; switch (selection) case 1: *blockSize = 1024; break; case 2: *blockSize = 4096; break; case 3: *blockSize = 5120; break; case 4: *blockSize = 6144; break; case 5: *blockSize = 15488; break; }; if ------- \n"); ( ((blockSize) % error = 1; lcm) 0) ------------- \n"); source.c printf("I ** error: block size not divisible by %d **\n", lcm); else printf(" ** invalid selection **\n"); }; } /* /* selectBlockSize */ ------------------------ SIMULATION COMMAND FUNCTION ---------- function: genBits Generate bits randomly. If the list is empty when this function is called, add the new bits to the list. If the list is not empty when this function is called, then simply replace the bits in the list with new bits. Because other functions may have added bits to the list, delete remaining elements after enough bits are generated. Input - linked list pointer linkList* llist int numBits - number of bits to generate Output Modify llist void genBits(linkList* llist, int numBits) int i, j; char *bits; listElement *currEl, *headPtr, *tailPtr; /* If list already exists, go through the list and replace values. This way, we don't have to delete elements, then add elements again. */ bits = (char*) calloc((*llist) .valueLength, currEl = (*llist).head; for (i=G; i < (numBits/ (*llist) for (j=O; j < sizeof (char)); .valueLength); i++) (*llist).valueLength; j++) bits[ji = (char) (rand)) % 2); if (currEl != NULL) (*currEl).value[j] = bits[j]; I; if (currEl == NULL) addLLbits(llist, bits); else currEl = (*currEl).next; }; /* if terminateEncoder) function may have appended bits to llist, so set tail to currEl->prev and delete the remaining elements */ (currEl NULL) != headPtr = (*llist).head; tailPtr = (*currEl).prev; (*tailPtr).next = NULL; (*llist).head = currEl; deleteList(llist); /* only actually deletes elements starting at currEl */ (*llist).head = headPtr; (*llist).tail = tailPtr; }; free(bits); /* /* genBits */ ----------------------- DISPLAY/DEBUG FUNCTION ---------------------- function: showArrayBits Show the bits in an array. Input Output Modify : char bits[] int size ~ array of bits length of array / 137 SIMULATION CODE 138 void showArrayBits(char bits[], int i; for (i=0; i<size; i++) printf("%d", printf ("\n"); } /* showArrayBits */ int size) (int)bits[i]); interleave.h interleave.h file: interleave.h This header file contains variable and function definitions for interleavers. #include <stdio.h> /* type definitions */ typedef struct interleaverVarsStruct int blockSize; int* } permutePattern; linkList* input; linkList* output; interleaverVars; /* selection function */ void selectInterleaver(interleaverVars* interleaver, int blockSize); simulation command function */ /* void doInterleave(interleaverVars* interleaver); /* init/deallocation functions */ void randomPatternInitVars(interleaverVars* interleaver, int numEl); void SrandomInitVars(interleaverVars* interleaver, int S1, int S2, int blockSize, int seed); int interleaverFileInitVars(interleaverVars* interleaver, FILE* fp, int numEl); void deinterleaverInitVars(interleaverVars* deinterleaver, interleaverVars* interleaver) void freeInterleaverVars(interleaverVars* interleaver); /* display/debug function */ void showPermutePattern(interleaverVars* interleaver) other functions */ /* void regroupltoK(linkList* thelist, int k); void regroup Ktol(linkList* thelist, int k); void outputPattern(interleaverVars* interleaver, FILE* fp); /* AT&T 2-step semi-random interleaver functions */ char canFillRest(int remainingIndices[], int index, interleaverVars* interleaver, int index, int deltalndex); void updateRemaining(int remainingIndices[], void genSrandomPattern(interleaverVars* interleaver, int Si, int S2); int S1, int S2); 139 140 SIMULATION CODE interleave.c file: interleave.c This source file contains code for all interleavers. #include "linkedlist.h" #include "interleave.h" #include <math.h> #include <stdio.h> #include <stdlib.h> * ------------ SELECTION FUNCTION - - function: selectInterleaver Selects the type of interleaver to use in generating a permutation pattern. interleaverVars* interleaver - interleaver pointer numEl int interleaver depth Input Output Modify interleaver void selectInterleaver(interleaverVars* interleaver, int numEl) f int selection; char error; int Sl, S2, seed; char filename[80]; FILE* fp; printf("I---------------------------------------------------------\n"); printf("I\n"); printf("l printf("I printf("I 1 2 Random permutation \n"); Two-step semi-random algorithm 3 (AT&T RN-029 standard)\n"); Read permutation pattern from file\n"); printf("\n"); printf(" ---------------------------------------------------------\n"); error = 1; while (error) printf("I Select interleaver type\n"); printf("I >> "); scanf("%d", &selection); if ((selection >= 1) & (selection <= 3)) error = 0; switch (selection) case 1: srand(151); randomPatternInitVars(interleaver, numEl); break; case 2: /* semi-random interleaver Uses function: SrandomInitVars() to init interleaver printf(" --------------------------------printf("I\n"); printf("I ** %d elements **\n", numEl); printf("I\n"); Sl, printf("I S2, seed\n"); printf("I -----------printf("I 1 (16, 6, printf("I 2 (18, 12, printf("I 3 (23, 12, printf("I 4 (25, 15, printf("I 5 (26, 15, printf(" \n"); \n"); 151)\n"); 151)\n"); 151\n"); 151)\n"); 151)\n"); printf("I--------------------------------- --------------printf("I Select semi-random interleaver parameters\n"); error = 1; while (error) printf("I >> "); scanf("%d", &selection); if ((selection >= 1) error = 0; & (selection <= 5)) -\n"); interleave.c switch (selection) 1: Sl case = 16; = 6; seed = 151; S2 break; case 2: 51 18; = 12; seed = 151; = S2 break; case 3: Sl S2 = 23; = 12; seed = 151; break; case 4: Sl = 25; S2 = 15; seed = 151; break; case 5: Sl S2 = 26; = 15; seed = 151; break; }; /* switch semi-random * printf("I generating interleave pattern...\n"); printf ("I note: if this takes too long, restart with smaller parameters\n"); SrandomInitVars(interleaver, Sl, S2, numEl, seed); /* if selection ok */ else printf(" I ** invalid selection **\n"); }; /* while error */ break; case 3: /* get interleave pattern from a file Uses function: interleaverFileInitvars) printf("I---------------------------------------------------------\n"); printf("I Enter input file name for printf("I >> "); to init %d elements\n", interleaver numEl); scanf("%s", filename); fp = fopen(filename, "r"); if (fp == NULL) error = 1; printf(" * I Unable to access file %s **\n", filename); else error = interleaverFileInitVars(interleaver, fp, fclose(fp); }; /* break; switch selection*/ /* if interleaver selection ok */ else printf("I ** invalid selection **\n"); }; /* ) while invalid selection, error==l */ /* selectinterleaver */ * ------------- SIMULATION COMMAND FUNCTION ------------------- f* /* function: doInterleave Performs element by element interleaving on an input linked list and stores the permuted elements in an output linked list. If the output list already exists, simply replace the element arrays with new values. Otherwise, add new elements to the output list. This function does not alter the input linked list. Input : Output Modify : interleaverVars* interleaver - interleaver pointer (*interleaver).output */ void doInterleave(interleaverVars* int i, interleaver) j; char* tempchar; twoDSignalPoint* tempPt; listElement **tempEl, *currEl, *outEl; numEl); 141 SIMULATION CODE 142 instead of deleteList((*interleaver).output), traverse /* list and replace values tempEl = calloc((*interleaver).blockSize, sizeof(listElement*)); (listElement**) */ /* makes sure input and output list valueLengths are the same (*(*interleaver).output).valueLength = (*(*interleaver).input).valueLength; currEl = (*interleaver).input->head; /* get memory locations of individual list elements for input */ for (i = 0; i < (*interleaver).blockSize; i++) tempEl[(*interleaver).permutePattern[i]] currEl = (*currEl).next; = currEl; i = 0; outEl = (*interleaver).output->head; while (i < (*interleaver).blockSize) if /* add value and signal as a new element to list * (outEl == NULL) tempchar = (*tempEl[i]).value; tempPt = (*tempEl[i]).signal; addLLdata((*interleaver).output, tempchar, tempPt); i++; /* replace value and signal of current output element */ else { for (j=o; j < (*interleaver).input->valueLength; (*outEl).value[j] (*outEl).signal[j] j++) (*tempEl[i]).value[j]; (*tempEl[i]).signal[j]; }; outEl - (*outEl).next; i++; }; free(tempEl); I; /* doInterleave */ */ INIT/DEALLOCATE FUNCTIONS function: randomPatternInitVars : interleaverVars* interleaver Input Sl int interleaver pointer minimum permute distance Output Modify : *interleaver void randomPatternInitVars(interleaverVars* interleaver, int numEl) int i; int rndNum, numDone; int* numbers; numbers = (int*)calloc(numEl, sizeof(int)); for (i=0; i < numEl; numbers[i] = i; i++) (*interleaver).blockSize = numEl; (*interleaver).permutePattern = (int*) calloc((*interleaver).blockSize, sizeof(int)); (*interleaver).output = (linkList*) calloc(l, sizeof(linkList)); llistInitVars((*interleaver).output, 0); for (numDone=0; numDone < (numEl-1); numDone++) rndNum = rand() % (numEl - numDone); (*interleaver).permutePattern[numDone] = numbers[numDone + numbers[numDone + rndNum] = numbers[numDone]; i; (*interleaver) .permutePattern~numEl-l] free(numbers); } /* randomPatternInitVars * function: SrandomInitVars = numbers (numEl-l]; rndNum]; interleave.c Initializes variables and generates an interleave pattern according to the semi-random algorithm from ITU proposal RN-029. See for description : Input Output : MOdify : function genSrandomPattern) for details. interleaver - interleaver pointer - minimum permute distance - minimum rearranged distance interleavervars* int int S1 S2 int int blockSize seed - number of elements to interleave - random number generator seed (*interleaver).permutePattern void SrandomInitVars(interleaverVars* interleaver, int Si, int S2, int blockSize, int seed) (*interleaver).blockSize = blockSize; permutePattern = (int*) calloc( (*interleaver) (*interleaver) output = (linkList*) calloc(1, sizeof(linkList)) (*interleaver) 1listInitVars((*interleaver).output, .blockSize, sizeof (int) 0); srand(seed); genSrandomPattern(interleaver, Sl, S2); } /* */ SrandomInitVars function: deinterleaverInitVars irterleaver Initializes a de-interleaver based on an existing The permute pattern of the de-interleaver permute pattern. the inverse of that of the interleaver. interleaverVars* deinterleaver - deinterleaver pointer Input interleaverVars* Output Modify interleaver - interleaver deinterleaver, interleaver) .blockSize; .blockSize = (*interleaver) permutePattern for (i=O; i < = (int*) calloc( (*deinterleaver) .blockSize, sizeof(int) (*interleaver).blockSize; i++) .permutePattern[i]] .permutePattern[(* interleaver) (*deinterleaver) .output = (linkList*) calloc(1, llistinitVars((*deinterleaver).output, 0); (*deinterleaver) /* interleaverVars* i; (*deinterleaver) (*deinterleaver) } pointer deinterleaver void deinterleaverInitVars(interleaverVars* int is deinterleaverInitVars = i; sizeof(linkList)) */ function: interleaverFileInitVars Reads an interleave pattern from a file. The first entry of the file is the interleaver depth and must match input parameter numEl. The remaining entries are the permutation indices. Input interleaverVars* interleaver FILE* fp int numEl interleaver pointer pointer of file to read from interleaver depth Outpu t : Modif y : interleaver int interleaverFileInitVars(interleaverVars* interleaver, FILE* fp, int numEl) int i=O; int size; int error; fscanf(fp, "%d", &size); if (size == numEl) /* file has correct interleaver depth */ error = 0; (*interleaver).blockSize = size; (*interleaver) permutePattern = (int*) for (i=0; i < fscanf(fp, calloc( (*interleaver) blockSize, (*interleaver) blockSize; i++) "Id", &(*interleaver) (*interleaver) .output = (linkList*) llistInitVars((*interleaver).output, .permutePattern[i] calloc(l, 0); sizeof(linkList)) sizeof(int)) 143 SIMULATION CODE 144 /* else file has wrong interleaver depth error = * 1; printf("\n** Input file contains incorrect interleaver depth **\n\n"); }; return error; /* interleaverFileInitVars */ function: freeInterleaverVars Free dynamically allocated memory for interleaver. Input : interleaverVars* interleaver - interleaver pointer Output Modify : interleaver */ void freeInterleaverVars(interleaverVars* interleaver) free((*interleaver).permutePattern); freeList((*interleaver).output); /* DISPLAY/DEBUG FUNCTIONS ------------- /* / freeInterleaverVars ------------------- /* function: showPermutePattern Print the permute pattern to the screen. : interleavervars* interleaver - interleaver pointer Input Output Modify */ void showPermutePattern(interleaverVars* interleaver) int i; printf("Permute pattern: "); for (i=0; i < (*interleaver).blockSize;i++) printf(" printf(" -\n"); } /* showPermutePattern */ OTHER FUNCTIONS ------------- /* (*interleaver).permutePattern[i]); %d", ------------------- function: regroupltoK The input linked list contains 1 bit per list element. This function regroups the list so that there are K bits per list element. The resulting list will have a factor of K less list elements. Input : linkList* thelist - linked list pointer - number of bits per list element k int Output Modify : thelist "I void regroup ltoK(linkList* thelist, int k) int num, i, j; char *bits; twoDSignalPoint* values; (*thelist).valueLength = k; bits = (char*) calloc(k, sizeof(char)); values = (twoDSignalPoint*) calloc(k, sizeof(twoDSignalPoint)); num = (int) (getNumElements(thelist) / k); for (i=O; i < num; i++) for (j=O; j < k; j++) interleave.c bits[j] = *getValue(thelist, 1); values[j] = *getSignal(thelist, 1); deleteFirstElement(thelist); addLLdata(thelist, bits, values); 1; free(bits); /* regroup ltoK * function: regroupKtol The input linked list contains K bit per list element. This function regroups the list so that there is 1 bit per list The resulting list will have a factor of K more element. list elements. : Input linked list pointer number of bits per list element linkList* thelist int k Output Modify : thelist void regroup Ktol(linkList* thelist, int k) { int num, i, j; char bit; twoDSignalPoint point; (*thelist).valueLength = 1; num = getNumElements(thelist); for (i=O; i < num; i++) f for { (j=0; j < k; bit j++) = (getValue(thelist, point = (getSignal addLLdata(thelist, 1)) [IjI; (thelist, 1)) [j]; &bit, &point); deleteFirstElement(thelist); /* regroupKtol s/ /* function: outputPattern Prints the interleaver depth and permute pattern to a file. interleaverVars* FILE* Input interleaver fp interleaver pointer output file pointer Output Modify : fp void outputPattern(interleaverVars* interleaver, FILE* fp) int i=O; fprintf (fp, "%d\n", (*interleaver) .blockSize) while (i < (*interleaver).blockSize) (*interleaver) .permutePattern[i++]); "%d\n", fprintf(fp, /* /* outputPattern */ AT&T 2-STEP SEMI-RANDOM INTERLEAVER FUNCTIONS ------------- -- --- */ function: canFillRest Determines whether the remaining unused permute indices can satisfy the semi-random algorithm. If not, then the algorithm will need to be restarted. Input : int remainingIndices[] int index - array to keep track of which permute indices have been used and which are available - index of remainingIndices[ that divides used and 145 SIMULATION CODE 146 available permute indices - element by element minimum separation distance int Sl - cell by cell minimum separation distance int S2 interleaverVars* interleaver - interleaver pointer ;1 Output if s-random algorithm can be completed, */ char canFillRest(int remainingIndices[], 0 otherwise int index, interleaverVars* interleaver, int S1, S2) int int i, j, possIndex; char canFilll=O, canFill2=0; i = index; while ( (!canFilll 11 !canFill2) && (i < (*interleaver).blockSize) possIndex = remainingIndices[i++]; canFill2 = (abs(possIndex-index) > if (canFill2) /* /* S2); ) /* check both Sl and S2 criteria check next permute index check S2 criterion */ /* compare to Sl previous cells j = (index <= Sl) ? 0 : (index-Sl); canFilll = 1; while (j < index) */ /* see if permute index can satisfy /* the semi-random algorithm */ canFilll = canFilll && (abs((*interleaver).permutePattern[j) - possIndex) > Sl); }; 1; return }; /* (canFilll && canFill2); canFillRest */ function: updateRemaining This function is used by enSrandomPattern. The array remainingIndices[] has "blockSize" cells and contains the permute indices. remainingIndices[O..(indexx-1)] contains permute indices that have already been used. remainingIndices[index..(bblockSize-1)] contains those that haven't been used. This function updates rem iningIndices[] when another permute index is used. Input : int remainingIndi ces [] int index int deltaIndex Output Modify array to keep track of which permute indices have been used and which are available index of *remainingIndices that divides used and available permute indices remainingIndices[index + deltaIndex] contains the permute index just used remainingIndices void updateRemaining(int remainingIndices[], int index, int deltaIndex) int swapIndex, swapValue; swapIndex = index + deltaIndex; swapValue = remainingIndices[swapIndex]; remainingIndices[swapIndex] = remainingIndices[index]; remainingIndices[index] = swapValue; }; /* updateRemaining /* index of used permute index /* the used permute index * /* update *remainingIndices * * function: genSrandomPattern(void) Generates a semi-random sequence of dist*inct integers between 0 and (blockSize-1). p(i) denotes the permuted sequence. Excerpt from RN-029: "Each randomly selected integer p(i) is compared with the previous selections We also > Sl. p(j) to check that if (i-j) <= S1 then pi(i)-pi(j) insist that p) must satisfy li-p(i) > S2." In the end we will need "blockSize" distinct numbers from 0.. (blockSize-1) Thus, we may find that with a few numbers left to choose from, we are unable to satisfy the algorithm criterion and may have to start the process over from The functions canFillRest() and updateRemaining() are used to the beginning. perform this check. Input Output void genSrandomPattern(interleaverVars* { int index, Sindex, j, deltaIndex; interleaver, int S1, int S2) * * * interleave.c int* while remainingIndices (index < for (j=O; = (int*) calloc((*interleaver) blockSize, < (*interleaver) /* /* all new numbers initialize *remainingIndices */ % ((*interleaver).blockSize-index); /* choose from remaining permute indices */ Sindex = remainingIndices[index + deltaIndex]; if (abs(Sindex - index) > S2) j = (index <= Sl) ? 0 : while /* /* (index-Sl); ((abs((*interleaver) .permutePattern[j] get the unused permute index check S2 criterion */ /* compare with S1 - && (j Sindex) > S) * previous indices */ < index)) j++; if (j == index) /* (*interleaver).permutePattern[index new permute index is okay to use = Sindex; updateRemaining(remainingIndices, index, deltaIndex); index++; }; }; }; /* }; /* while we can fill the rest of the permutation pattern while not done generating Sindex's free(remainingIndices); }; /* genSrandomPattern */ */ S2)) (canFillRest(remainingIndices, index, interleaver, S1, deltaIndex = rand) don't have .blockSize;j++) remainingIndices[j]=j; index = 0; while sizeof(int)) (*interleaver).blockSize) j 147 */ */ / SIMULATION CODE 148 conv.h file: convRoutines.h This header file contains variable and function definitions for convolutional encoders. */ /* type definitions */ typedef struct branchStruct int Sl, S2; int d; int p; branch; typedef struct encoderVarsStruct branch** trellis; int* terminator; char* int k; int int linkList* linkList* encoderVars; state; r; V; input; output; selection function */ /* void selectEncoder(encoderVars* encoder, int kSel); /* simulation command function */ void encodeBlock(encoderVars* encoder); void terminateEncoder(encoderVars* encoder); */ init/deallocate functions /* void duplicateEncoder(encoderVars* encoderl, encoderVars* encoder2); void ratel2initVars(encoderVars* encoder, char fbPoly[], char ffPoly[]); void ratekkllnitVars(encoderVars* encoder, char fbPoly[], char** ffPoly, void CCinitVars(encoderVars* encoder, int degree); void freeEncoderVars(encoderVars* encoder); int k); /* display/debug function */ void showTrellis(encoderVars* encoder); generator polynomial functions */ /* void convertoctal(char octalChar, char binaryRep[]); void convertString(char polyString[}, char coeffArray[], int v); int getDegree(char polyString[]); int v); void setFeedback(char* polyString, char* feedbackCoefficient, void setFeedforwG(int parityNum, int inputNum, char* polyString, char*** forwCoefficient, int v); trellis functions */ /* void ratelnSetBranch(branch* br, char*** ffCoeff, char* fbCoeff, int v, int r) void ratekklSetBranch(branch* br, char*** ffCoeff, char* fbCoeff, int v, int k); nextState(encoderVars* encoder, listElement* inputEl); int conv.c conv.c file: convRoutines.c This source file contains code for convolutional encoder. */ #include "general.h" #include "linkedlist.h" #include "conv.h" #include #include #include #include <math.h> <stdio.h> <string.h> <stdlib.h> ---- ---------- function: ----------------- SELECTION FUNCTION - */ selectEncoder Called from the main program to select an encoder from a specified list of options. Input encoderVars* encoder int kSel pointer to encoder struct number of input bits to encoder Output Modify : encoder void selectEncoder(encoderVars* encoder, int kSel) int selection, k; char error; char *fbPoly, **ffPoly; if ((kSel >= 1) && (kSel <= 3)) /* kSel= 1 rate-1/2 2 rate-2/3 3 rate-3/4 switch (kSel) { case 1: /* rate-1/2 code */ ffPoly = (char**) calloc(1, sizeof(char*)); printf("----------------------------------------printf("j\n"); printf(" I printf(" I 1 2 fbPoly = 23 fbPoly = 7 ffPoly = 35\n"); ffPoly = 5\n"); printf(" I 3 fbPoly = 15 ffPoly = 17\n"); printf("I\n"); printf("|--------------------------------------- printf ("I select generator polynomials\n"); error = 1; while (error) >> printf("I scanf("%d", if "); &selection); >= 1) & (selection ((selection error = switch <= 3)) 0; (selection) case 1: fbPoly ffPoly[0] break; case 2: fbPoly ffPoly[O] = "23"; = "35"; = "5"; = "7"; break; case 3: fbPoly ffPoly[0] /* else = = "15"; "17"; break; }; /* switch rate-1/2 */ if rate-1/2 select ok / printf("I ** invalid selection **\n"); }; /* while select rate-1/2 error */ ratel2initVars(encoder, fbPoly, ffPoly[0]); break; /* break case 1: rate-1/2 */ 149 SIMULATION CODE 150 case 2: /* */ rate-2/3 code ffPoly = (char**) calloc(2, sizeof(char*)); --------------- printf("I--------------------------------------printf("I\n"); printf("I 1 rate-2/3 fbPoly = 23, ffPolyl -- 35, \n"); ffPoly2 = 27\n") ; printf("I\n"); -----------printf("I---------------------printf("I Select generator polynomials\n"); error = 1; while (error) printf(" >>" scanf("%d", &selection); if ((selection >= 1) & (selection <= 1)) 0; error switch (selection) case 1: = fbPoly ffPoly[0] ffPoly[l] = "31"; = "27"; = "35"; break; }; /* switch rate-2/3 */ / if rate-2/3 select ok */ else printf ("I ** invalid selection **\n"); }; /* while select rate-2/3 error = 2; k ratekklInitVars(encoder, fbPoly, ffPoly, k); */ break; /* break case 2: rate-2/3 case 3: /* rate-3/4 code */ ffPoly = (char**) calloc(3, sizeof(char")); printf("I--------------------------------printf("\n"); rate-3/4 -printf("| 1 printf(" \n"); fbPoly = 13 ffPolyl printf("I--------------------------------------printf("I Select generator polynomials\n"); error = 1; while (error) printf("j 11, >> ((selection >= 1) & (selection <= 1)) error = 0; switch (selection) case 1: fbPoly = "15"; ffPoly[O] = "11"; ffPoly[l] = "13"; ffPoly[2] } /* else }; /* = "17"; break; }; /* switch rate-3/4 */ if rate-3/4 select ok */ printf("I ** invalid selection **\n"); while select rate-3/4 error */ k = 3; ratekklInitVars(encoder, fbPoly, ffPoly, k); */ break; /* break case 3: rate-3/4 /* /* / /* switch rate select */ free(ffPoly); if kSel in correct range */ selectEncoder */ ------------------ SIMULATION COMMAND FUNCTION ---------------- function: encodeBlock This function encodes an entire block of bits. is called from the simulation main program. This function : encoderVars* encoder - pointer to encoder struct Input Output Modify : encoder output contains parity bits void encodeBlock(encodervars* encoder) int i, j; char* parityBits; listElement *inputEl, *currEl, *headPtr, *tailPtr; = 15, ffPoly3 ---------------- \n") ; scanf("%d", &selection); if ffPoly2 = 17\n"); 151 conv.c parityBits = (char*)calloc((*encoder).r, sizeof(char)); currEl = (*encoder).output->head; /* access output list elements inputEl = (*encoder).input->head; /* access input list elements for (i=0; i < (*encoder).v; i++) (*encoder).state[i] = 0; while (inputEl != NULL) /* set initial state to 0 /* go through entire */ */ */ input list */ intToCharArray(nextState(encoder, inputEl), parityBits, (*encoder).r); /* generate parity bits for (j=0; j < (*encoder).r; j++) /* add parity bits to output list */ if (currEl == NULL) addLLbits ((*encoder) output, &parityBits(j]) else { (*currEl).value[0] = parityBitsfj; currEl = (*currEl).next; }; inputEl = (*inputEl).next; }; /* now delete the rest of the output list, which may have extra elements appended by terminateEncoder() */ if (currEl != NULL) headPtr = (*encoder).output->head; tailPtr = (*currEl).prev; (*tailPtr).next = NULL; (*encoder).output->head = currEl; deleteList((*encoder).output); /* only actually deletes elements starting at currEl "/ 1; (*encoder).output->head = headPtr; (*encoder).output->tail = tailPtr; }; /* encodeBlock */ /* function: terminateEncoder Generates bits to terminate the encoder to zero state. It does this by using the trellis termination tree, stored in array terminator[]. The terminating bits are added to the encoder input list, and the corresponding parity bits are added to the encoder output. encoderVars* encoder - pointer to encoder struct Input Output Modify encoder.input, encoder.output void terminateEncoder(encoderVars* encoder) int S,d, bit; char* temp = (char*) calloc((*encoder).k, sizeof(char)); /* need v bits to terminate, there are k bits / branch, so v/k branches. now if v/k is not an integer, then we'll have ceil(v/k) branches and ceil(v/k) * k bits. */ for (bit = 0; bit < ((*encoder).v / (*encoder) .k); bit++) S = charArrayToInt((*encoder).state, d = (*encoder).terminator[S]; intToCharArray((*encoder) trellis[S] addLLbits((*encoder).input, temp); intToCharArray((*encoder) trellis [S] addLLbits((*encoder).output, temp); intToCharArray((*encoder) trellis [S) /* printf("term state: (*encoder).v); [d] .d, temp, (*encoder) .k); [d] .p, temp, (*encoder) .r) [d] .S2, (*encoder) state, %d\n", (*encoder) .trellis[S] [d] .S2); *. free(temp); }; /* terminateEncoder /* ------------------ */ INIT/DEALLOCATE FUNCTION -------------------- function: duplicateEncoder This function initializes a second encoder using the parameters / (*encoder) .v) */ 152 SIMULATION CODE of a first encoder. encoderVars* encoderl encoderVars* encoder2 Input pointer to original encoder struct pointer to duplicate encoder struct Output encoder2 Modify void duplicateEncoder(encoderVars* encoderl, encoderVars* encoder2) { int state, inp; (*encoder2).k = (*encoderl).k; CCinitVars(encoder2, (*encoderl).v); for (state = 0; state < pow(2, (*encoder2).v) ; state++) (*encoder2).terminator[state] = (*encoderl).terminator[state]; (inp = 0; inp < pow(2, (*encoder2).k); inp++) (*encoder2).trellis[state] [inp] = (*encoderl).trellis[state)[inp]; for }; /* duplicateEncoder * function: ratel2initVars This function initialize s convolutional encoder variables for a rate-1/2 encoder. It allocates memory for all struct variables, and uses the rate-/n encoder structure to Erncoder generator functions are already generate the trellis. stored in input arrays flbPoly[] and ffPoly[]. Input : encoderVars* en;coder fbiPoly[] char char ff1 Poly[} - pointer to encoder struct - string describing feedback polynomial - string describing feedforwrad polynomial Output Modify : encoder void ratel2initVars(encoderVars* encoder, char fbPoly[], char ffPoly[]) int i, j; char*** forwCoefficient; char* feedbackCoefficient; int numStates; int* done; linkList statelist; twoDSignalPoint S, newS; /* indices /* /* holds feedforward coefficients holds feedback coefficients */ */ /* note: will ffPoly always have the correct degree?? (*encoder).k = 1; CCinitVars(encoder,getDegree(ffPoly)); /* rate-l/2 encoder, k = one input */ feedbackCoefficienf = (char*) calloc ((*encoder).v + 1, sizeof(char)); setFeedbackG(fbPoly, feedbackCoefficient, (*encoder).v); /* allocate memeory * forwCoefficient = (char***) calloc for (i=0; i < (*encoder).r; i++) /* */ ((*encoder).r, sizeof(char**)); allocate memory forwCoefficient[ii = (char**) calloc ((*encoder).k, sizeof(char*)); for (j=0; j < (*encoder).k; j++) forwCoefficient [i] [j] = (char*) calloc ( (*encoder) .v + 1, sizeof (char)); /* (v+l) feedforward coefficients */ }; setFeedforwG(l, 1, ffPoly, forwCoefficient, (*encoder).v); numStates = (int)pow(2, (*encoder).v); for (i=0; i < numStates; i++) for (j=0; j < pow(2, (*encoder).k); j++) ratelnSetBranch(&(*encoder) trellis[i] (*encoder).r); [j], forwCoefficient, feedbackCoefficient, llistInitVars(&statelist, 1); S.i = 0; /* set first element in statelist to be zero state, then search backwards * addLLsignal(&statelist, &S); done = (int*) calloc(numStates, sizeof(int)); for (i=0; i < numStates; i++) done[i] = 0; /* find trellis termination path for each state / while(notEmpty(&statelist)) S = getSignal(&statelist, 1) [0]; deleteFirstElement(&statelist); /* now find all states that transition to S */ for (i=0; i < numStates; i++) for (j=0; j < pow(2, if (*encoder).k); j++) (((*encoder).trellis[i} [j].S2 == S.i) && (done[i) == 0)) done[i] = 1; (*encoder) .v, 153 conv.c = j; (*encoder).terminator[i] newS.i = i; addLLsignal(&statelist, &newS); }; /* now know how to terminate trellis from any state -- use for (i=O; i < (*encoder) (j=G; j for terminator[] as encoder input until zero state reached */ (*encoder).r; i++) < (*encoder).k; j++) free(forwCoefficient[i] [ji); free(forwCoefficient[i]); free~feedbackCoefficient); free(done); S/* rate1nitVars '/ function: ratekklInitVars This function initializes convolutional encoder variables for a rate-k/(k+l) encoder. Encoder generator functions are already in input arrays fbPoly[] and ffPoly[] [I Input Output Modify encoderVars* encoder - pointer to encoder char char fbPoly[) ffPoly[] - string describing feedback polynomial - string describing feedforwrad polynomial struct encoder void ratekklInitVars(encoderVars* encoder, char fbPoly[, char** ffPoly, int k) { int i, j; int /* indices */ numStates; int* done; linkList statelist; twoDSignalPoint S, newS; char*** forwCoefficient; /* holds feedforward coefficients */ /* each forwCoefficient[r] [k] [] holds the generator coefficient for parity bit # r and input bit # k. forwCoefficient[r] [k] [0] corresponds to a 1, and forwCoefficient[r] [k] [v] corresponds to D'v */ char* /* feedbackCoefficient; holds feedback coefficients (*encoder).k = k; /* note: will ffPoly always have the correct degree?? CCinitVars(encoder,getDegree(ffPoly[o()); /* */ rate-k/(k+) feedbackCoefficient = (char*) calloc ((*encoder) .v + 1, sizeof (char)); setFeedbackG(fbPoly, feedbackCoefficient, (*encoder) .v); forwCoefficient = for (i=0; i < (char***) encoder */ */ /* allocate memeory calloc ( (*encoder) .r, sizeof (char**)); /* allocate memory */ */ (*encoder).r; i++) { forwCoefficient [i] = (char**) calloc ((*encoder) .k, sizeof (char*) for (j=O; j < (*encoder).k; j++) forwCoefficient[i] [jI = (char*) calloc ((*encoder) .v + 1, sizeof (char)); coefficients */ }; for (i=O; i < (*encoder).k; i++) (i+l) , ffPoly[i] , setFeedforwG(l, numStates = (int)pow(2, forwCoefficient, /* (*encoder) .v) i] [j , forwCoefficient, feedbackCoefficient, llistInitVars(&statelist, 1); S.i = 0; /* set first element in statelist to be zero state, then search backwards '/ addLLsignal(&statelist, &S); done = (int*) calloc(numStates, sizeof(int)) (i=0; i done[i] /* < numStates; = 0; i++) find trellis termination path for each state */ while(notEmpty(&statelist)) S = getSignal(&statelist, 1) [01; deleteFirstElement(&statelist); /* now find all states that transition to S */ for (i=0; i < numStates; i++) for (j=0; j feedforward (*encoder).v); for (i=O; i < numStates; i++) for (j=0; j < pow(2, (*encoder).k); j++) ratekklSetBranch(&(*encoder) .trellis (*encoder).k); for (v+l) < pow(2, (*encoder).k); if (((*encoder) .trellis[i] done[i] = 1; j++) [j] .S2 == S.i) && (done[i] == 0)) (*encoder) .v, SIMULATION CODE 154 (*encoder).terminator[i] = j; newS.i = i; addLLsignal(&statelist, &newS); }; now know how to terminate trellis from any state /* -for use (*encoder) .terminator[] as encoder input until zero state reached */ (i=O; i < (*encoder).r; i++) (j=O; j < (*encoder).k; j++) free(forwCoefficient[i][jl); free(forwCoefficient[i]); for }; free(feedbackCoefficient); free(done); /* ratekklInitVars */ /* function: CCinitVars Initializes class variables for any rate-k/(k+l) convolutional code. Assumes only 1 parity bit. encoderVars* encoder - pointer to encoder struct degree - highest degree of generator polynomials int Input Output Modify encoder void CCinitvars(encoderVars* encoder, int degree) int i , j, states, branches; (*encoder).r = 1; (*encoder).v = degree; /* only 1 parity bit (*encoder).output = (linkList*) calloc llistInitVars((*encoder).output, 1); */ (1, sizeof(linkList)); (*encoder) .state = (char*) calloc((*encoder).v, sizeof (char)); for (i=O; i < (*encoder).v; i++) (*encoder).state[i] = 0; states = (int)pow(2, (*encoder).v); (*encoder).terminator = (int*) calloc(states, sizeof(int)); branches = (int)pow(2, (*encoder).k); = (branch**) calloc(states, sizeof (branch*)) (*encoder) .trellis for (i=O; i < states; i++) f = (branch*) calloc(branches, sizeof (branch)); (*encoder) .trellis[i] for (j=0; j < branches; j++) (*encoder).trellis[i] ][j.Sl =i; (*encoder).trellis[i] [j].d =j; }; }; /*CCinitvars function: */ freeEncoderVars Deallocates memory for encoder struct variables. : encoderVars* encoder Input Output Modify : encoder. void freeEncoderVars(encoderVars* encoder) int m, states, branches; free((*encoder).state); states = (int)pow(2, (*encoder).v); branches = (int)pow(2, (*encoder).k); for (m=0; m < states; m++) free((*encoder).trellis[m]); free((*encoder).trellis); freeList((*encoder).output); /* freeEncoderVars */ conv.c DISPLAY/DEBUG ---------------- FUNCTION ---------------------- / function: showTrellis Prints the trellis description to the screen. encoderVars* encoder - point to encoder struct variables Input Output Modify void showTrellis(encoderVars* encoder) S,d, int states, branches; states = (int)pow(2, (*encoder).v); branches = (int)pow(2, (*encoder).k); printf("Sk-1 dk pk Sk\n"); "---- printf( for -- ------ printf("%d }; /* ---- \n"); (S=O; S < states; S++) for (d=O; d < branches; d++) showTrellis %d %d %d\n", (*encoder).trellis[S] [d].SI, (*encoder).trellis(S} [d].d, (*encoder).trellis[S] [d].p, (*encoder).trellis[S] [d].S2); */ /* ------------------- GENERATOR POLYNOMIAL FUNCTIONS function: convertOctal This function converts the string representation of an octal number into binary. octal character, so 0 .. 7 - 3 cell array to hold binary representation char octalChar char binaryRep[] Input Output Modify - binaryRep */ void convertOctal(char octalChar, char binaryRep[E) *binaryRep = 0; /* MSB of octal value = 0; *(binaryRep+2) = 0; if ((octalChar == '1') 1 (octalChar == *(binaryRep+2) = 1; if ((octalChar == '2') 1 (octalChar == *(binaryRep+l) = 1; */ *(binaryRep+l) if } /* ((octalChar == '4') *binaryRep = 1; convertOctal 1 (octalChar == /* LSB of octal value (octalChar == '5') I (octalChar == '7')) '3') (octalChar == ( '6') (octalChar == '7')) '5') (octalChar == '6') (octalChar == '7')) '3') | */ */ function: convertString This function converts the string representation of a generator polynomial into a char array. char polyString] char coeffArray] Input Output Modify : - string representation of polynomial - array of polynomial coefficients, O's and l's coeffArray */ void convertString(char polyString[E, char coeffArray[], int v) int i, length; char* temp; length = strlen(polyString) ; temp = (char*) calloc (3*length, for (i=0; ialength; i++) convertOctal(polyString[i], for (i=0; i<(v+l); i++) /* sizeof(char)); &(temp[3*i])); number of octal digits */ alloc ate max number of binary digits /* /* conver t each octal digit, */ store in temp[] 155 SIMULATION CODE 156 coeffArray[i) = temp[i+(3*length-(v+l))]; } /* transfer last (v+l) values to coeffArray /* free up memory used by temp[] free(temp); convertString / V / function: getDegree This function finds the degree of a generator polynomial. The degree of the polynomial is its highest exponent value. char polyString{) - generator polynomial string - degree of the polynomial (tempDeg-1) Input Output Modify */ int getDegree(char polyString[I) int length, tempDeg; char* temp = (char*) calloc(3, sizeof(char)); /* temporary variable to hold 3 binary values /* number of octal characters in string */ /* all octal characters except first have 3 bits /* convert first octal char into binary * length = strlen(polyString); tempDeg = 3* (length-1); convertOctal(polyString[O], temp); if (temp[0) == 1) tempDeg += 3; else if (temp[l] == 1) tempDeg += 2; else if (temp)2] == 1) tempDeg++; /* first octal char has no leading zeros /* first octal char has 1 leading zero /* first octal char has 2 leading zeros / * * free(temp); /* tempDeg = v+l. return (tempDeg-1); /* Return (tempDeg-1) = v * */ getDegree function: setFeedbackG This function converts the string representation of the feedback generator polynomial into char array. Input char polyString] char feedbackCoefficient[] int v Output Modify - feedback polynomial string - array holding binary coefficients - degree of polynomial feedbackCoefficient */ void setFeedbackG(char polyString[], char feedbackCoefficient[], int v) convertString(polyString,&feedbackCoefficient[0], S /* show conversion results int i, length; v); / length = strlen(polyString); printf("Feedback -- "); for (i=0;i<length;i++) printf("%d", (int)polyString[i]); printf(" : "); for (i=0;i<(v+l);i++) printf("%d", (int)feedbackCoefficient[i}); printf ("\n") S/* setFeedbackG * function: setFeedforwG This function converts the string representation of a generator polynomial into charean and calculates the degree of the polynomial. Input : int parityNum int inputNum char polyString] char*** forwCoefficient int v Output : Modify : forwCoefficient - indicate which parity bit the polynomial is for - indicate which input bit the polynomial is for - the polynomial string - array holding binary coefficients - degree of polynomail * * conv.c void setFeedforwG(int parityNum, int inputNum, char polyString[] char*** forwCoefficient, convertString(polyString, &forwCoefficient [parityNum-1] [inputNum-1 S /* show conversion results int i, length; [0], int v) v) */ length = strlen(polyString); printf("parity bit %d, input bit %d, G%d%d -- " parityNum, inputNum, parityNum, inputNum); for (i=O;i<length;i++) printf("%d", (int)polyString[i]); printf(" : "); for (i=O;i<(v+l);i++) printf ("%d", (int) forwCoefficient[parityNum-1] [inputNum-1] [i]) printf ("\n") */ setFeedforwG / TRELLIS --------- */ FUNCTIONS -------- function: ratekklSetBranch This function takes in k input bits and an encoder state and finds 1 parity bit and the next state. The first delay register corresponds to state[0] and the last state register corresponds to state[v-1. Input : Output : Modify : branch* char*** char* int int br ffCoeff fbCoeff v k - pointer to a Trellis branch the feed-forward generator polynomials the feedback generator polynomial degree number of input bits br void ratekklSetBranch(branch* br, char*** ffCoeff, char* fbCoeff, int v, int k) int i, numInputs; char* state = (char*) calloc (v, sizeof(char)); char* feedbackCoefficient = fbCoeff; char*** forwCoefficient = ffCoeff; char y; char* inputBits; inputBits = (char*) calloc(k, sizeof(char)); intToCharArray((*br).Sl, state, v); intToCharArray((*br).d, inputBits, k); /* y = (last state) + sum (all inputs with generator value 1) */ y = state[v-1]; for (numInputs=0; numInputs < k; numInputs++) y = y ^ (forwCoefficient[I] [numInputs] [0] & inputBits[numInputs]); // The carrot ^ is the "xor" operator. // state[i] = (state[i-11) + (y if feedback generator has term D^(v-i)) D^ (v-i)) for (i=(V-1); i>O; i--) { state[i] = state[i-1] ^ (y & feedbackCoefficient[v-i]); for 1; (numInputs=0; numInputs < k; numInputs++) state[i] = state[i] ^ (forwCoefficient [0] [numInputs] [v-i] + sum (all inputs with generator value & inputBits [numInputs] // first state = (y if feedback generator has term D'v) + sum (all inputs with generator value Dv) state[0] = (y & feedbackCoefficient[v]); for (numInputs=0; numInputs < k; numInputs++) state [0] = state [0] ^ (forwCoefficient [0] [numInputs] [v] & inputBits [numInputs] (*br).S2 = (*br).p = charArrayToInt(state, v); Oxl && y; free(state); free(inputBits); } // ratekklSetBranch function: ratelnSetBranch 157 158 SIMULATION CODE This function takes in 1 input bit and an encoder state and The first delay finds (n-1) parity bits and the next state. and register corresponds to state[0] corresponds to state[v-1]. br anch* br ch ar* ffCoeff fbCoeff ch ar* in t int r : Input v Output Modify state the last register pointer to a Trellis branch the feed-forward generator polynomials the feedback generator polynomial degree number of parity bits br char*** ffCoeff, void ratelnSetBranch(branch* br, int i, char* fbCoeff, int v, int r) numOutputs; /* temporarily hold statel value; char stateiTemp; * char* state = (char*) calloc (v, sizeof(char)); char* feedbackCoefficient = fbCoeff; char-*- forwCoefficient = ffCoeff; char input; char* y; y = (char*) calloc(r, '* y[] sizeof(char)); - hold r parity bit values */ intToCharArray((*br).S1, state, v); input = ((*br).d == 0) ? 0 : 1; state = /* first (input bit) + sum (all states i for which feedback has term D^i) statelTemp = input; for (i=l; i<(v+l); i++) stateiTemp = statelTemp ^ (state [i-1] && feedbackCoefficient [i] /* output y[j] = sum (all states i for which there is a feedforward term D^i in polynomial j) for (numOutputs = 0; numOutputs < r; numOutputs++) = statelTemp & forwCoefficient[numOutputs] [0] [0] (i=l; i<(v+l); i++) y[numOutputs] = y[numOutputs] ^ (state[i-1] &&forwCoefficient[numOutputs] / y[numOutputs] for [0] [i); /* state[i] = state[i-1] */ for (i=(v-1); i>0; i--) state[i] = state[i-1}; /* finally set first state to temporary holding value state[O] = stateOTemp; (*br).S2 = charArrayToInt(state, (*br).p = charArrayToInt(y, r); * v); free(state); /* ratelnsetBranch * function: nextState This function trans itions a convolutional encoder to the next state. It takes in put bit(s) from the encoder input, transitions the encoder, and re turns the parity bit generated by the transition. s* encoder point to encoder struct pointer to element with input bits listElemen t* inputEl parity bit from state transition encoder.st ate encodervar Input Output Modify int nextState(encoderVars* encoder, listElement* inputEl) int /a S,d; branch: S input = d, ---------- > S2 parity = p / S = charArrayToInt((*encoder).state, (*encoder).v); d = charArrayToInt((*inputEl) value, (*(*encoder) input) .valueLength); /* index 0 of value contains MSB, index (valueLength-1) contains LSB */ intToCharArray((*encoder) trellis[S] [d].S2, (*encoder).state, (*encoder).v) ; return (*encoder).trellis[S] [d].p; /* nextState */ /* update encoder state puncture.h puncture.h /* file: puncture.h This header file contains variable and function definitions of puncturing objects. /* type definitions * typedef struct punctureVarsStruct int numInputs; int punctureLength; int currIndex; char** puncturePattern; linkList** linkList* input; output; } puncturevars; typedef struct unPunctureVarsStruct int numOutputs; int int punctureLength; currIndex; char-- puncturePattern; linkList* input; linkList* output; } unPunctureVars; /* simulation command functions */ void punctureBlock(punctureVars* puncturer) void unpunctureBlock(unPunctureVars* unpuncturer); /* init/deallocate functions */ void twoPunctureInitVars(punctureVars* puncturer, void threePunctureInitVars(punctureVars* char patternl)], puncturer, char pattern2[]); char patternl[], void unPunctureInitVars(unPunctureVars* unpunc, punctureVars* punc); void freePunctureVars (punctureVars* puncturer); void freeUnpunctureVars (unPunctureVars* unpuncturer) /* other functions */ char* stringTochar(char theString[], int punctureLength); char punctureBits (punctureVars* puncturer, listElement** inputEl) char pattern2[], char pattern3[]); 159 160 SIMULATION CODE puncture.c file: puncture.c This source file contains code for puncture functions. #include "linkedlist.h" #include "puncture.h" #include <stdio.h> #include <stdlib.h> #include <string.h> /* ------------------------ SIMULATION COMMAND FUNCTIONS ---- function: punctureBlock This function punctures bits at the input linked lists. The puncture pattern is stored in the puncturer struct. This function is called from the main simulation program. : Input punctureVars* puncturer - pointer to puncturer struct Output Modify : output linked list void punctureBlock(punctureVars* puncturer) char result; int i, j, len; listElement **inputEl, *currEl; (*puncturer).currIndex = 0; = (*puncturer).output->head; inputEl = (listElement**) calloc((*puncturer).numInputs, sizeof(listElement*)); for (j=O; j < (*puncturer).numInputs; j++) currEl = inputEl[j} (*puncturer).input[j]->head; len = getNumElements((*puncturer).input[}); for (i=0; i < len; i++) result = punctureBits(puncturer, /* inputEl); result > 1 means that all bits were punctured */ if (result <= 1) (currEL == NULL) addLLbits((*puncturer).output, it &result); else (*currEl).value[() = result; currEl = (*currEl).next; for (j=S; j < (*puncturer).numInputs; j++) inputEl[j] = (*inputEl[j]).next; }; free(inputEl); I /* punctureBlock function: unpunctureBlock This function unpuncturers the input linked list into multiple output linked lists. The unpuncture pattern is the same as the puncture pattern and is stored in the unpuncturer struct. This function is called from the main program. Input : unPunctureVars* unpuncturer Output Modify : output linked lists 161 puncture.c void unpunctureBlock (unPunctureVars* unpuncturer) int i; twoDSignalPoint LLR, puncturedLLR; listElement **outputEl, *currEl; char done; outputEl = (listElement**) calloc((*unpuncturer) (i=0; i outputEl[i] for currEl = numOutputs, (*unpuncturer).numOutputs; i++) = (&(*unpuncturer) output [i])->head; < sizeof(listElement*) (*unpuncturer).input->head; puncturedLLR.i = 0; puncturedLLR.q = 0; (*unpuncturer).currIndex = /* The puncture 0; structure could be: currIndex 0123 1000 0010 When currIndex = 3 and (*unpuncturer) input is empty, we still unpuncturer the last LLR, which will be 0 for both bits. loop condition must take this case into account. alternatively currIndex 012 If blockSize=1024, index=1 when 100 010 001 The condition to end is the input list if requesting an input element. need to So the while we will be at (*unpuncturer).input runs out of elements. is already empty and we are still */ done while = 0; (done == 0) for = (i 0; i < (*unpuncturer).numOutputs; i++) LLR = puncturedLLR; /* at most one bit will be not punctured... no more than one if ( (*unpuncturer) .puncturePattern[i] [(*unpuncturer) */ currIndex] == 1) if (currEl == NULL) done = 1; else = (*currEl).signal[0]; currEl = (*currEl).next; LLR }; if (done == if 0) (outputElti] == NULL) addLLsignal(&(*unpuncturer).output[ii, &LLR); else *(outputEl[i]->signal) outputEl[i] = 1; }; /* currIndex ); /* /* if = LLR; outputEl[i]->next; */ for */ (*unpuncturer) .currIndex */ = ((*unpuncturer) .currIndex + 1) % (*unpuncturer) .punctureLength; while */ free(outputEl); } /* unpunctureBlock ------------------------- function: */ INIT/DEALLOCATE FUNCTION ------------ ------ */ twoPunctureInitVars Initializes class variables and sets up puncturing pattern for two input linked lists. Input : punctureVars* puncturer char patternl[] char pattern2[] - pointer to puncture struct - string specifying first pu ncture pattern - string specifying second p uncture pattern /* update SIMULATION CODE 162 Output : Modify : puncturer void twoPunctureInitVars(punctureVars* puncturer, char patternl[], char pattern2[]) f (*puncturer).numInputs = 2; (*puncturer).punctureLength = strlen(patternl); (*puncturer).currIndex = 0; (*puncturer).puncturePattern = (char**) calloc (2, sizeof(char*)); (*puncturer) .puncturePattern[0] = stringTochar(patternl, (*puncturer) .punctureLength) (*puncturer) .punctureLength) (*puncturer) .puncturePattern[1] = stringTochar(pattern2, (*puncturer).input = (linkList**) calloc(2, sizeof(linkList*)); (*puncturer).output = (linkList*) calloc(l, llistInitVars((*puncturer).output, 1); } /* twoPunctureInitVars function: sizeof(linkList)); */ twoPunctureInitVars Initializes class variables and sets up puncturing pattern for two input linked lists. : punctureVars* char char char Output : Modify : puncturer Input puncturer patterni[] pattern2[l] pattern3[] - pointer to puncture struct string specifying first puncture pattern string specifying second puncture pattern string specifying third puncture pattern */ void threePunctureInitVars(punctureVars* puncturer, char patternl[], char pattern2[], char pattern3 [] (*puncturer).numInputs = 3; (*puncturer).punctureLength = strlen(patternl); (*puncturer).currIndex = 0; (*puncturer).puncturePattern = (char**) calloc (3, sizeof(char*)); stringTochar(patternl, stringTochar(pattern2, (*puncturer) .punctureLength) (*puncturer) .punctureLength) (*puncturer) .puncturePattern[2] = stringTochar(pattern3, (*puncturer) .punctureLength) (*puncturer) .puncturePattern[0] = (*puncturer) puncturePatternl] = (*puncturer).input = (linkList**) calloc(3, (*puncturer).output = (linkList*) calloc(l, llistInitVars((*puncturer).output, 1); /* sizeof(linkList*)); sizeof(linkList)); threePunctureInitVars */ function: freePunctureInitVars Deallocates memory for puncture struct variables. : punctureVars* puncturer - pointer to puncture struct Input Output Modify : puncturer void freePunctureVars(punctureVars* puncturer) int i; for (i=0; i < (*puncturer).numInputs; i++) free((*puncturer).puncturePattern[i]); free((*puncturer).puncturePattern); freeList((*puncturer).output); S/* freePunctureVars */ function: unPunctureInitVars Initializes variables in unpuncturer struct by using variable values in puncture struct. puncture.c Input Output Modify unPunctureVars* unpunc - pointer to unpuncturer struct punctureVars* - pointer to puncturer struct punc unpunc */ void unPunctureInitVars(unPunctureVars* unpunc, punctureVars* punc) int i; (*unpunc).numOutputs = (*punc).numInputs; (*unpunc).punctureLength = (*punc).punctureLength; (*unpunc).currIndex = 0; (*unpunc) .puncturePattern = (char**) calloc( (*punc) numInputs, sizeof(char*) (*unpunc) output = (linkList*) calloc( (*unpunc) .numOutputs, sizeof(linkList) for (i=0; i < (*punc).numInputs; i++) I (*unpunc) .puncturePatterntil = (char*) calloc ((*unpunc) punctureLength, (*unpunc) .puncturePatternfi] = (*punc) .puncturePattern[i] llistInitVars(&(*unpunc).outputti], sizeof (char)); 1); }; /* unPunctureInitVars */ /* function: freeUnpunctureInitVars Deallocates memory for unpuncturer unPunctureVars* Input Output Modify struct - unpuncturer variables. pointer to unpuncturer struct unpuncturer void freeUnpunctureVars(unPunctureVars* unpuncturer) int i; /* this routine causes problems for (i=0; i < -- don't use (*unpuncturer).numOutputs; i++) for now */ free((*unpuncturer).puncturePattern[i]); freeList(&(*unpuncturer).output[ii); }; free((*unpuncturer).puncturePattern); /* freeUnpunctureVars */ * -------- ---------- ----- OTHER FUNCTIONS ----------------------- / /* function: stringTochar Moves a string of punctureLength. Input O's and int into a char array of size - input puncture pattern string char theString[] Output Modify punctureLength - array containing */ char* stringTochar(char O's size of array and l's theString[H, char* charPtr = (char*) int l's int punctureLength) calloc(punctureLength, for (i=O; i<punctureLength; i++) charPtr[i] = (theStringi] == '0') return charPtr; /* sizeof (char)); i; stringTochar I function: */ punctureBits ? 0 : 1; /* decide if the character is a 0 */ 163 SIMULATION CODE 164 Performs bit puncturing according to specified pattern. Input punctureVars* puncturer - pointer to puncturer struct - pointers to input elements listElement** inputEl Output Modify value of the puncturing operation (0 or 1) */ char punctureBits(punctureVars* puncturer, listElement** inputEl) int index, i; char temp; char pout; pout = 0; index = (*puncturer).currIndex; (*puncturer) currIndex = ((*puncturer) currIndex + 1) % (*puncturer) .punctureLength; */ temp = if (*puncturer).puncturePattern[0][index]; (i=1; i < for (*puncturer).numInputs; i++) temp = temp II (*puncturer) .puncturePattern[i] [index]; /* not all of the bits are punctured */ (temp) pout = *(inputEl[0]->value) * (*puncturer).puncturePattern[0][index]; for (i=1; i < (*puncturer).numInputs; i++) (* (inputEl[i) ->value) * (*puncturer) .puncturePattern[i[ [index]); pout = pout else pout = 2; return pout; /* punctureBits */ /* update currIndex mapper.h mapper.h file: mapper.h This header file contains variable and function definitions for bit mappers and symbol mappers. #include <stdio.h> /* type definitions * typedef struct bitMapVarsStruct int b; char* pattern; char* symbolBits; int numUncoded, numSys, numParity; } bitMapVars; typedef struct oneDsymbolMapVarsStruct int M; double* symbols; char** code; double Eav; } oneDsymbolMapVars; typedef struct twoDsymbolMapVarsStruct int M; twoDSignalPoint* symbols; char** code; double Eav; } twoDsymbolMapVars; typedef struct twoDsignalMapVarsStruct bitMapVars bitMapper; twoDsymbolMapVars symbolMapper; linkList* inputU; linkList* inputS; linkList* inputP; linkList* output; } twoDsignalMapvars; /* selection functions / void selectBitMapper(bitMapVars* map); void selectSignalMapper(twoDsignalMapVars* map); /* simulation command function */ void mapBlock(twoDsignalMapVars* signalMap); /* init/deallocate functions */ void bitMapInitVars(bitMapVars* bitMapper, char* pattern); void normalMappingInitVars(twoDsignalMapVars* signalMapper, int numHalfBits) void QAMgcSignalMapInitVars(twoDsignalMapVars* signalMapper, int InumGBI, int InumGB2, int QnumGB1, int QnumGB2); void freeBitMapVars(bitMapVars* bitMap); void freeTwoDsymbolMapVars(twoDsymbolMapVars* map); void freeTwoDsignalMapVars(twoDsignalMapVars* signalMap); /* display/debug functions */ void showoneDmapping(oneDsymbolMapVars* mapper); void showTwoDmapping(twoDsymbolMapVars* mapper, FILE* fp); /* bit mapper functions */ void getNumBits(bitMapVars* bitMap); symbol mapper functions */ /* void PAMgcSignalSet(oneDsymbolMapVars* mapper, int numdrayBitsl, int numGrayBits2); void QAMSignalSet(twoDsymbolMapVars* symbolMapper, oneDsymbolMapVars tempI, oneDsymbolMapVars tempQ); double* createPAMlevels(int M); twoDSignalPoint formTwoDsymbol (twoDsignalMapVars* signalMapper) double findOneDsymbol(char* bits, oneDsymbolMapVars* map); twoDSignalPoint findTwoDsymbol(char* bits, twoDsymbolMapVars* map); /* gray code functions */ linkList* makeNbitGC(int numBits); linkList* concatCodes(linkList *MSBlist, linkList *LSBlist) char** createGrayCode(int numMSB, int numLSB, int M); 165 SIMULATION CODE 166 mapper.c file: This mapper.c source file contains code for the signal mapper. #include #include #include #include "general.h" "linkedlist.h" "source.h" "mapper.h" #include #include #include #include <stdlib.h> <stdio.h> <string.h> <math.h> SELECTION FUNCTION function: selectBitMapper This function is called from the main simulation program to select a bit map pattern for uncoded simulations. Input : bitMapVars* map - pointer to bit mapper Output Modify map void selectBitMapper(bitMapVars* map) int selection; char error; char *pattern; printf(" printf("\n"); .----------------- printf("| 1 printf("I 2 printf("I printf("I ss ssss 3 4 ssssss ssssssss ----- \n"); ------------- 4-pt uncodecd simulation\n"); 16-pt\n"); 64-pt\n"); 256-pt\n"); printf("I\n"); printf("I-----------------------------printf("I ---------------- select bit map pattern\n"); error = 1; while (error) printf("j >> ); scanf("%d", &selection); if ((selection >= 1) & (selection <= 4)) I error = 0; switch (selection) case 1: pattern = "ss"; break; case 2: pattern = "ssss"; break; case 3: pattern = "ssssss"; break; case 4: pattern = "ssssssss"; break; ; /* switch selection */ bitMapInitVars(map, pattern); /* /* if selection ok */ else printf("| ** invalid selection **\n"); while error S/* selectBitMapper */ mapper.c function: selectSignalMapper This function is called from the main simulation program to select a signal mapping. Input : twoDsignalMapVars* map Output Modify map */ void selectSignalMapper(twoDsignalMapVars* map) int half, pts, Igcl, Igc2, Qgcl, Qgc2; int selection; char error; pts = (int)pow(2, (*map).bitMapper.b); half = (int) ((*map).bitMapper.b/2); printf("------------------------------------printf("I\n"); printf("| 1 printf(" j 2 printf(" 3 -------------------- \n" normal bit labeling (00, 01, 1 0, ll)\n"); full gray labeling in each dime nsion (00, 01, 11, 10)\n"); concatenated gray labeling in each dimension\n"); printf ("\n"); printf("------------------------------------error = 1; while (error) printf("I Select labeling scheme\n" printf(I >> "); scanf("%d", &selection); if ((selection >= & (selection 1) <= 3)) { error = 0; switch (selection) case 1: normalMappingInitVars(map, half); break; case 2: Igcl = half; Igc2 = 0; Qgcl = half; Qgc2 = 0; QAMgcSignalMapInitVars(map, Igcl, Igc2, Qgcl, Qgc2); break; case 3: ------------printf(" -----------------------------------------\n"); printf("| * number of upper bits in each dimension must be <= %d **", printf("I printf(I \n"); ENTER:\n"); printf(" I Number of upper bits in I dimension\n"); printf("I >> "); scanf("%d", &Igcl); Igc2 = half - Igcl; printf("I Number of lower bits in I dimension = %d\ n", Igc2) printf("I \n"); printf(I Number of upper bits in Q dimension\n"); printf ("I >> ); scanf("%d", Qgcl); Qgc2 = half - Qgcl; printf(" I Number of lower bits in Q dimension = %d\ n", Qgc2); /* Igcl, Qgc2 must each be <= half / if (Igcl > half) printf("I ** I parameters invalid: Il > %d \n", half); error = 1; }; if (Qgcl > half) { printf("I ** Q parameters invalid: error = 1; Ql > %d **\n", half); }; if (error == 0) QAMgcSignalMapInitVars(map, Igcl, printf("\n"); break;. }; /* else }; }; /* switch selection if input ok / printf(" /* while error * / invalid selection **\n"); } /* selectSignalmapper * SIMULATION COMMAND FUNCTION Igc2, Qgcl, Qgc2); half); 167 SIMULATION CODE 168 mapslock function: The function is called from the main simulation program to map a block of bits to two-D symbols. twoDsignalMapVars* signalMapper Input Output Modify signalMapper output linked list void mapBlock(twoDsignalMapVars* signalMapper) twoDSignalPoint symbol; listElement *currEl, *Uhead, *Shead, *Phead; function formTwoDsymbol() changes the I ead pointer of each input linked list, so we temporarily store the real ones Iere */ Shead = (*signalMapper).inputS->head; /* no trellis coded modulation * if ((*signalMapper).inputU != NULL) Uhead = (*signalMapper).inputU->head; /* if ((*signalMapper).inputP != NULL) Phead = (*signalMapper).inputP->head; uncoded simulation no parity bits */ currEl = (*signalMapper).output->head; while ((*signalMapper).inputS->head 1= NULL) symbol = formTwoDsymbol(signalMapper); if (currEl == NULL) addLLsignal((*signalMapper).output, else &symbol); (*currEl).signal[0] = symbol; currEl = (*currEl).next; }; /* function formTwoDsymbol() changes the head pointer of each input linked list, so we restore the real ones here */ (*signalMapper).inputS->head = Shead; if ((*signalMapper).inputU if ((*signalMapper).inputP != NULL) (*signalMapper).inputU->head = Uhead; !=NULL) (*signalMapper).inputP->head = Phead; }; /* mapBlock * INIT/DEALLOCATE FUNCTIONS function: bitM apInitVars Initialize bit mapper variables. Input : DitMa char pvars* DitMapper - pointer to nit mapper struct pattern[] - array containing bit map pattern Output Modify : bitMa pper void bitMapInitVars(bitMapVars* bitMapper, char pattern[]) int i; (*bitMapper).b = strlen(pattern); (*bitMapper).pattern = (char*) calloc((*bitMapper).b, sizeof(char)); for (i=0; i < (*bitMapper).b; i++) (*bitMapper).pattern[i] = pattern[i]; (*bitMapper).symbolBits = (char*) calloc((*bitMapper).b, sizeof(char)); getNumBits(bitMapper); } /* bitMapInitVars function: * freeBitMapVars Deallocate memory for bit mapper struct variables. Input bitMapvars* bitMap - Output Modify bitMap pointer to bit mapper struct mapper.c void freeBitMapVars(bitMapVars* bitMap) free((*bitMap).pattern); free((*bitMap).symbolBits); /* freeBitMapVars */ function: freeTwoDsymbolMapVars Deallocates memory for symbol mapper struct variables Input Output Modify twoDsymbolMapVars* map - pointer to symbol mapper struct map void freeTwoDsymbolMapVars(twoDsymbolMapVars* map) int i; free((*map).symbols); for (i=O; i < (*map).M; i++) free((*map).code[i]); free((*map).code); } /* freeTwoDsymbolMapVars */ function: normalMappingInitVars Input : twoDsymbolMapVars* signalMapper pointer to symbol mapper struct Output Modify : map void normalMappingInitVars(twoDsignalMapVars* signalMapper, int numHalfBits) int i; oneDsymbolMapVars tempI, tempQ; tempI.M = (int)pow(2,numHalfBits); templ.symbols = createPAMlevels(tempI.M); tempI.code = (char**)calloc(tempI.M, sizeof(char*)); tempQ.M = (int)pow(2,numHalfBits); tempQ.symbols = createPAMlevels(tempQ.M); tempQ.code = (char**)calloc(tempQ.M, sizeof(char*)); for (i=0; i < tempI.M; i++) temp{.code[i] = (char*)callocnumalfgits, sizeofchar) tempQ.code[i] = (char*)calloc(numHalfBits, sizeof(char)); intToCharArrayi, temp.code[i], numHalfBits) intToCharArray(i, tempQ.code[i], numHalfBits); QAMSignalSet(&(*signalMapper).symbolMapper, tempI, tempQ); /* these might not be initialized in case of uncoded simulation */ (*signalMapper).inputU = NULL; (*signalMapper).inputP = NULL; (*signalMapper) output = (linkList*) calloc(l, llistInitVars((*signalMapper).output, 1); sizeof(linkList)); } /* normalMappingInitVars */ /* function: QAMgcSignalMapInitVars This function initializes the signal mapper by using concatenated gray labels with a square-QAM signal set. : twoDsignalMapVars* int int int int Output : Modify : signalMapper Input signalMapper InumGB1 InumGB2 QnumGB1 QnumGB2 - pointer to upper gray lower gray upper gray lower gray signal mapper struct bits in I channel bits in I channel bits in Q channel bits in Q channel 169 SIMULATION CODE 170 I void QAMgcSignalMapInitVars(twoDsignalMapVars* signalMapper, int InumGB1, int InumGB2, int QnumGB, oneDsymbolMapVars tempI, tempQ; PAMgcSignalSet(&tempI, InumGB1, InumGB2); PAMgcSignalSet(&tempQ, QnumGB1, QnumGB2); QAMSignalSet(&(*signalMapper).symbolMapper, tempI, tempQ); /* these might not be initialized in case of uncoded simulation * (*signalMapper).inputU = NULL; (*signalMapper).inputP = NULL; (*signalMapper).output = (linkList*) calloc(l, sizeof(linkList)); llistInitVars((*signalMapper).output, 1); /* QAMgcInitVars * function: freeTwoDsignalMapVars Deallocates memeory for variables in signal mapper struct. twoDsignalMapVars* signalMap Input Output Modify : signalMap */-. void freeTwoDsignalMapVars(twoDsignalMapVars* signalMap) freeBitMapVars(&(*signalMap).bitMapper); freeTwoDsymbolMapVars(&(*signalMap).symbolMapper); freeList((*signalMap).output); /* --------------------- function: DISPLAY/DEBUG FUNCTIONS ---------------------- showOneDmapping Shows points and labels for one-dimensional signal sets. : oneDsymbolMapVars* mapper - pointer to signal mapper struct Input Output Modify */ void showOneDmapping(oneDsymbolMapVars* mapper) int i, j, b; b = (int) (loglO((*mapper).M)/loglo(2)); printf(" (amplitude, label) : "); for (i=O; i < (*mapper).M; i++) printf("(%g,", (*mapper).symbols[i]); for (j=S; j < b; j++) printf("%d", (int) (*mapper).code[i] j]); printf(") "); }; printf ("\n") /* showOneDmapping * function: showTwoDmapping Shows points and labels for two-dimensional signal sets. If fp is NULL, then prints to screen. Otherwise prints to file fp. Input : twoDsymbolMapVars* mapper - pointer to symbol mapper struct FILE* fp - pointer to output file Output Modify : contents of file fp void showTwoDmapping(twoDsymbolMapVars* mapper, FILE* fp) int i, j, b; * int QnumGB2) 171 mapper.c b = (int) (logl0((*mapper).M)/log10(2)); if (fp NULL) != fprintf(fp, "[point, label] for (i=0; i < (*mapper).M; i++) fprintf(fp, "[(%gg), ", (j=0; j b; j++) fprintf(fp, "%d", (int) fprintf(fp, " " (*mapper) .symbols[i] .i, (*mapper) .symbols[i] .q); for (*mapper) code [i] [j]); }; fprintf (fp, "\n"); else printf(" [point, label] for (i=0; i < (*mapper).M; i++) printf(" [(%g,%g) , ", (*mapper) .symbols[i .i, (*mapper) for (j=0; j < b; j++) printf("%d", (int) (*mapper) .code[i [i); printf("] symbols[i .q) }; ("\n") printf }; /* /* / showTwoDmapping ------- --- function: getNumBits ---- BIT MAPPER FUNCTIONS ---------------------- / Find the number of uncoded, systematic, and parity bits per symbol. Input bitMapVars* bitMap Output Modify *bitMap - pointer to bit mapper struct */ void getNumBits(bitMapVars* bitMap) int i; (*bitMap).numParity = 0; (*bitMap).numSys = 0; (*bitMap).numUncoded = 0; for (i=0; i < (*bitMap).b; i++) if ((*bitMap).pattern[i] == /* 'u') (*bitMap).numUncoded += 1; else if ((*bitMap).pattern[i] == 's') (*bitMap).numSys += 1; else (*bitMap).numParity += 1; /* /* getNumBits --- initialize symbolPattern[] /* count number of systematic bits 1; SYMBOL MAPPER FUNCTIONS ---------------------- function: PAMgcSignalSet Forms a one-dimensional signal set with odd-valued points centered evenly about zero. Input oneDsymbolMapVars* PAMmapper - pointer to symbol map struct int numGrayBitsl - upper gray bits for labels int numGrayBits2 - lower gray bits for labels Output Modify *PAMmapper */ void PAMgcSignalSet(oneDsymbolMapVars* PAMmapper, int numGrayBitsl, int numGrayBits2) (*PAMmapper) (*PAMmapper) .M = (int)pow(2, (numCrayBitsl + numGrayBits2)); symbols = createPAMlevels( (*PAMmapper) M); */ */ /* count number of parity bits per symbol */ -------- */ /* count number of uncoded bits per symbol */ 172 SIMULATION CODE code = createGrayCode(numGrayBitsl, (*PAMmapper) /* PAMgcSignalSet numGrayBits2, (*PAMmapper) .M) */ function: QAMSignalSet Forms a two-dimensional square signal set with odd-valued points centered evenly about zero in each direction. Input twoDsymbolMapVars* symbolMapper - pointer to symbol map struct oneDsymbolMapVars tempI - 1-D signal set for I channel - 1-D signal set for Q channel oneDsymbolMapVars tempQ Output Modify *symbolMapper void QAMSignalSet(twoDsymbolMapVars* symbolMapper, oneDsymbolMapVars tempI, oneDsymbolMapVars tempQ) int L1, L2, L; int i, j, k, pos; (*symbolMapper).Eav = 0; (*symbolMapper).M = tempI.M * tempQ.M; (*symbolMapper) code = (char**) calloc ( (*symbolMapper) M, sizeof (char*)) (*symbolMapper) symbols = (twoDSignalPoint*) calloc( (*symbolMapper) .M, sizeof (twoDSignalPoint) L = (int) (loglO((*symbolMapper).M)/loglO(2)); Ll = (int) (logb (tempI.M)/logl (2)); L2 = (int)(log10(tempQ.M)/log10(2)); for (i=0; i < tempI.M; i++) for (j=O; j < tempQ.M; j++) (*symbolMapper).code[i*tempQ.M + j) = (char*) calloc(L, sizeof(char)); (*symbolMapper).symbols[i*tempQ.M + j .i = tempI.symbols[i]; (*symbolMapper) .symbols [i*tempQ.M + j.q = tempQ.symbols[j]; (*symbolMapper).Eav += pos = (sqr(tempI.symbols[i}) + sqr(tempQ.symbols[j])); 0; for (k=O; k < Ll; k++) (*symbolMapper).code[i*tempQ.M + ji [pos++] = tempI.code[i] [k]; for (k=O; k < L2; k++) (*symbolMapper).code[i*tempQ.M + ji[pos++] }; (*symbolMapper).Eav /= /* = tempQ.code[j] [ki; }; QAMSignalSet function: (*symbolMapper).M; */ findOneDsymbol Finds the signal point in a one-D signal set with bit label matching the input bits. bits[] : char oneDsymbolMapVars* map Output : one-dimensional point Modify Input - array containing input bits - signal set points and labels double findOneDsymbol(char bits[], oneDsymbolMapVars* map) int i, j, componentBits; char* tempElement; . char done=O, bitsEqual; componentBits = (int) (log)((*map).M)/logl(2)); i = -1; while /* keep on looking for the match (!done) */ i++; tempElement = (*map).code[i]; /* get next Gray code element bitsEqual = 1; for (j=O; j<componentBits; j++) /* check each bit for the match */ bitsEqual = bitsEqual && (tempElementji == bits[j]); done = bitsEqual; }; return (*map).symbols[i]; } /* findOneDsymbol */ /* return the matched PAM level */ */ mapper.c function: findTwoDsymbol Finds the point in a two-D signal set that has the same bit label as the input bit array. Input char bits[] twoDsymbolMapVars* map Output - input bit array two-D points and labels two-D point Modify */ twoDSignalPoint findTwoDsymbol(char bits[], twoDsymbolMapVars* map) int i, j, componentBits; char* tempElement; char done=O, bitsEqual; componentBits = (int) (loglO((*map) .M)/loglO(2)); i = -1; /* while (!done) i++; tempElement bitsEqual = = (*map).code[i]; 1; for (j=O; j<componentBits; bitsEqual = bitsEqual /* j++) */ keep on looking for the match get next Gray code element /* check each bit for the match && (tempElement[j] * == bits[j}); done = bitsEqual; return (*map).symbols[i]; } /* findTwoDsymbol function: /* return */ the matched PAM level */ createPAMlevels Creates an array of odd-valued points centered evenly about zero. Input int M - number of total points Output double PAMlevels[] - array Modify */ double* createPAMlevels(int M) { int i; double *PAMlevels = (double*) calloc(M, sizeof(double)); levels */ for (i=O; i<(M/2); * allocate memory to hold PAM / i++) generate the M PAM levels */ { PAMlevels[i) = (double) (-l*(M-1) + 2*i) PAMlevels[M-1-i] = -l*PAMlevels[i]; /* the /* the negative (M/2) PAM levels po sitive (M/2) PAM levels */ }; return PAMlevels; }; /* createPAMlevels */ function: formTwoDsymbol Takes bits from the input linked lists and maps them to a signal point. Input Output Modify twoDsignalMapVars* signalMapper - pointer to signal mapper output linked list */ twoDSignalPoint formTwoDsymbol(twoDsignalMapVars* signalMapper) bitMapVars* map = &(*signalMapper) .bitMapper; int i; for (i=0; i < (*map).b; i++) if ((*map).pattern[i == 'u') (*map) .symbolBits[i] = (*signalMapper) .inputU->head->value [0] (*signalMapper).inputU->head = (*signalMapper) .inputU->head->next; else if ((*map).pattern[i] (*map) .symbolBits[i] (*signalMapper) == s') = (*signalMapper) .inputS->head->value [0]; inputS->head = (*signalMapper) .inputS->head->next; * 173 174 SIMULATION CODE else (*map).symbolBits[i] = (*signalMapper) .inputP->head->value [0; (*signalMapper).inputP->head = (*signalMapper).inputP->head->next; 1; return findTwoDsymbol ((*map) symbolBits, /* &(*signalMapper) symbolMapper) */ formTwoDsymbol - - GRAY CODE ------------- FUNCTIONS */ function: makeNbitGC Make a full gray code with the specified number of bits. Gray codes can be formed by a recursive algorithm starting with the 1-bit code 0,1. In general, a n-bit gray code can be formed from the (n-l)-bit code by prepending a '0' to the (n-l)-bit code, followed by prepending a 'l' to the (n-l)-bit code in reverse order. 0,1 -- > 0 + (0,I) and 1 + (1,0) = 00,01,11,10 00,01,11,10 -- > 0 + (00,01,11,10) and 1 + (10,11,01,00) = 000,001,011,010,110,111,101,100 and so on... int numBits - number of bits in the full gray code. *theList - the linked list of gray codes Input Output Modify */ linkList* makeNbitGC(int numBits) linkList = *theList (linkList*) calloc(1, sizeof(linkList)); /* linkList *tempList; listElement *tempElement; char *tempchar; int /* temporary variables allocate memory for the gray code */ i; llistInitVars(theList, numBits); if (numBits == /* base case of recursive algorithm 1) */ tempchar = (char*) calloc(l, sizeof(char)); *tempchar = 0; /* make a 2 element linked list with just /* 0 and 1 */ addLLbits(theList, tempchar); * = 1; *tempchar addLLbits(theList, tempchar); free(tempchar); } /* numBits = 1 */ else tempList = makeNbitGC(numBits-1); /* recurse -- first make a tempcnar = (cnar-) caliocnumBits, sizeot(cnar)); tempElement = getElement(tempList, 1); /* now prepend the 0 to the tempchar[0] = 0; /* the prepended 0 */ while (tempElement != NULL) (n-l)-bit gray code (n-l)-bit gray code */ */ for (i=l; i<numBits; i++) /* copy the remaining (n-1) bits */ tempchar[i] = (*tempElement).valueli-1]; addLLbits(theList, tempchar); /* add this n-bit code to the final linked list tempElement = '/ (*tempElement).next; }; /* get the last element of the (n-i)-bit gray code -tempElement = getElement(tempList, tempchar[O] = 1; while (tempElement != now prepend the 1 */ (int)pow(2, (*tempList) .valueLength)); /* the prepended 1 */ NULL) { for (i=l; i<numBits; i++) /* copy the remaining (n-1) bits */ tempchar[i] = (*tempElement).value[i-1]; addLLbits(theList, tempchar); /* add this n-bit code to the final linked list tempElement = (*tempElement).prev; /* go in reverse order */ deleteList(tempList); free(tempchar); }; /* return /* numBits >1 theList; makeNbitGC */ * /* free up temporary memory allocations */ */ */ mapper.c 175 function: concatCodes Concatenate two separate codes contained in separate linked lists The elements of the second code are appended to each element of the first code. Ex.: 0,1 concatenated with 00,01,11,10 becomes 000,001,011,010,100,101,111,110, which is not the same as a 3-bit full gray code. linkList *MSBlist - the first list of codes linkList *LSBlist - the second list of codes Output :theList - the list for the concatenated code Modify Input */ linkList* concatCodes(linkList *MSBlist, linkList *LSBlist) linkList *theList = (linkList*) calloc(l, * allocate memory for concat. list sizeof(linkList)); /* temporary variables int b = (*MSBlist) .valueLength+(*LSBlist) valuE ?Length; char *tempchar = (char*) calloc(b, sizeof(char int j; listElement *tempEll, *tempEl2; llistInitVars(theList, b); tempEll = getElement(MSBlist, 1); while (tempEll != /* NULL) (j=0; j<(*MSBlist) .valueLength; j++) tempchar~j] = (*tempEll).value[j]; for tempEl2 = getElement(LSBlist, while (tempEl2 != NULL) 1); get first element of first list */ /* continue concat until thru first list /* get bits from first - */ code go through entire second code / */ */ (j=0; j<(*LSBlist).valueLength; j++) /* get bits from second code */ tempchar[(*MSBlist).valueLength + j] = (*tempEl2).value[j]; addLLbits(theList, tempchar); /* add the new code to the final tempEl2 = (*tempEl2).next; for }; go through LSB gray code list /* */ tempEll = (*tempEll).next; }; /* go through MSB gray code list go to next element in first code */ deleteList(MSBlist); deleteList(LSBlist); return theList; } /* concatCodes */ function: createGrayCode Determine whether to create a full gray code (numMSB=O or numLSB=O) or a 2-level concatenated Gray code. The final Gray code is stored in a linked list int numMSB - number of bits in the upper level of the 2-level gray code int numLSB - number of bits in the lower level of the 2-level gray code Output : two-D array containing the gray code Modify Input */ char** createGrayCode(int numMSB, int numLSB, int M) linkList *MSBGC, *LSBGC, *finalGC; char** retArray = (char**) calloc(M, listElement *element; int i=O, j; if (numMSB == 0) finalGC = makeNbitGC(numLSB); else if (numLSB == 0) finalGC = makeNbitGC(numMSB); else sizeof(char*)) /* make a full (b/2)-bit gray code */ /* make a full */ (b/2)-bit gray code /* gray code for upper bits */ MSBGC = makeNbitGC(numMSB); LSBGC = makeNbitGC(numLSB); /* gray code for lower bits */ finalGC = concatCodes(MSBGC,LSBGC); /* concatenate the two codes */ element = (*finalGC).head; while (element != NULL) retArray[i] = (char*) calloc(numMSB+numLSB, for (j=O; j < (*finalGC) .valueLength; j++) retArray[i] [j] = (*element) .value[j]; sizeof(char)) */ list 176 SIMULATION CODE i++; element = (*element).next; 1; return retArray; S/* createGrayCode /* */ return the final gray code * channel.h channel.h file: channel.h This header file contains definitions for simulation functions related to sending signals through channels, including AWGN channels. */ /* type definitions */ typedef struct channelVarsStruct double nvariance; linkList* input; linkList* output; } channelVars; /* simulation command function s/ void sendAWGNblock(channelVars* channel); /* init/deallocate functions */ void channelInitVars(channelVars* channel); void freeChannelvars(channelVars* channel); other functions */ twoDSignalPoint sendAWGNpoint(channelVars* channel, twoDSignalPoint origPt); void Gauss32(double*, double*); /* 177 178 SIMULATION CODE channel.c file: channel.c This source file contains code for simulation functions related to sending signals through channels, including AWGN channels. */ #include "linkedlist.h" #include "channel.h" #include <math.h> #include <stdio.h> #include <stdlib.h> SIMULATION COMMAND FUNCTION function: sendAWGNblock This function takes all signal points from the channel input, sends them through an AWGN channel, and adds them to the channel output list. This function is called from the main simulation program void sendAWGNblock(channelVars* channel) int i, len; twoDSignalPoint point; listElement *inputEl, *currEl; inputEl = (*channel).input->head; currEl = (*channel).output->head; len = getNumElements((*channel).input); for (i=O; i < len; i++) point = sendAWGNpoint(channel, (*inputEl).signal[0)); if (currEl == NULL) addLLsignal((*channel).output, &point); else (*currEl).signal[0) = point; currEl = (*currEl).next; inputEl = (*inputEl).next; 1; } / /* senoAWUnoIock -/ ----------------------- INIT/DEALLOCATE FUNCTIONS function: channelInitVars This function initializes channel variables. void channelInitVars(channelVars* channel) (*channel).n variance = 1; (*channel).output = (linkList*) calloc (1, sizeof(linkList)); llistInitVars((*channel).output, 1); S/* channelInitVars */ function: freechannelVars This function deallocates channel variable memory. channel.c void freeChannelVars(channelVars* channel) freeList((*channel).output); /* freeChannelvars */ OTHER FUNCTIONS --- function: sendAWGNpoint This function takes a 2D signal point from the channel input, sends it through an AWGN channel, and adds it to the channel output list. */ twoDSignalPoint sendAWGNpoint(channelVars* channel, twoDSignalPoint origPt) twoDSignalPoint point; Gauss32(&(point.i), &(point.q)); point.i *= sqrt((*channel).n variance); point.i origPt.i; point.q sqrt((*channel).nvariance); point.q += origPt.q; return point; /* sendAWGNpoint Function: */ Guass32 Description: Generate two Gaussian random variables. 32 bit arithmetic. Assumes */ void Gauss32(double *x, double *y) f long temp; double ul,u2,r,theta,PI=3.1415; temp = rand(); /* rand) gen an integer temp = temp % RAND MAX; between 0 and RANDMAX '/ /* modulus operation to make sure value of TEMP is less than Ox7fffffff - which is the greatest value of a LONG data type ul = (double) temp / (double) RANDMAX; /* now convert range to 0 <= ul <1 temp = rand)); temp = temp % RAND MAX; u2 = (double) temp / (double) RAND MAX; /* ul must be double because log) r = sqrt(-2.0 * log(l.0-ul)); /* /* theta = u2 * 2 .0 * PI; /* *x = or * cos(theta)); *y = }/* or * sin(theta)); Gauss32 */ ' takes a double as an argument t */ 0 <= r < sqrt(-2*ln(l/RAND M AX)) */ with 0 < (1-ul) <= 1 (no zero) */ 0 <= theta < 2pi */ 179 180 SIMULATION CODE demod.h file: demod.h This header file contains definitions for functions that generate soft-values from received signal points for input to the MAP decoder. /* type definitions */ typedef struct demodulatorVarsStruct linkList* input; linkList* outputS; linkList* outputP; } demodulatorVars; /* simulation command function */ void demodulateTwoDBlock(demodulatorVars* dem, twoDsignalMapVars* mapper, double noisevar); void decode uncoded(linkList* decodedLL, twoDsignalMapVars* mapper, linkList* points); init/deallocate functions */ /* void demodulatorInitVars(demodulatorVars* dem); void freeDemodulatorVars(demodulatorVars* dem); /* other functions */ double calcBitLLR(twoDSignalPoint point, int pos, twoDsymbolMapVars symbolMapper, double noisevar) demod.c demod.c file: demod.c This source file contains code for generating soft-values from received signal points for input to the MAP decoder. #include #include #include #include "general.h" "linkedlist.h" "mapper.h" "demod.h" #include <stdlib.h> #include <stdio.h> #include <math.h> --------------------- SIMULATION COMMAND FUNCTIONS - ---------------------- function: demodulateTwuDBlock This function calculates the LLR of each bit in the received symbols. dem demodulatorVars* twoDsignalMapVars* mapper Input double Output Modify noise var - pointer to demodulator struct - pointer to signal mapper - channel noise variance dem void demodulateTwoDBlock(demodulatorVars* dem, twoDsignalMapVars* mapper, double noise var) int i, j, num; twoDSignalPoint LLR; listElement *outP, *outS, *ElPtr; outP = (*dem).outputP->head; outS = (*dem).outputS->head; ElPtr = (*dem).input->head; num = getNumElements((*dem).input); for (i=O; i < num; i++) for (j=O; j < (*mapper).bitMapper.b; j++) if ((*mapper).bitMapper.pattern[j] != 'u') LLR.i = calcBitLLR((*ElPtr).signal[0], j, (*mapper).symbolMapper, noise var); LLR.q = LLR.i; if ((*mapper).bitMapper.pattern[j] == 's') if (outS == NULL) addLLsignal((*dem).outputS, &LLR); else (*outS).signal[0] = LLR; outS = (*outS).next; else if ((*mapper).bitMapper.pattern[j] if (outP == NULL) == 'p') addLLsignal((*dem).outputP, &LLR); else (*outP).signal[0] = LLR; outP = (*outP).next; }; }; }; /* if s or p */ /* for loop: bits in a symbol */ ElPtr = (*ElPtr).next; 1; /* demodulateTwoDBlock */ function: decode_uncoded This function is used with uncoded QAM to get the upper bound 181 182 SIMULATION CODE for the QAM bit error rate. It chooses the constellation point nearest to the received symbol as the point that was sent. There is no struct associated with this operation. decodedLL - linkList* Input Output Modify list where decoded bits will be stored - pointer to signal mapper - list of symbol at output of channel twoDsignalMapVars* mapper points linkList* decodedLL void decode uncoded(linkList* decodedLL, twoDsignalMapVars* mapper, linkList* points) int i, min; double minD, D; twoDSignalPoint signal; deleteList(decodedLL); while (notEmpty(points)) signal = getSignal(points, 1) deleteFirstElement(points); [0]; min = 0; minD = sqrt(sqr(signal.i for - (*mapper).symbolMapper.symbols[0) .i) + sqr(signal.q - (*mapper).symbolMapper.symbols [0].q)) (i=l; i < (*mapper).symbolMapper.M; i++) D = sqrt(sqr(signal.i - (*mapper).symbolMapper.symbols[i] .i) + sqr(signal.q - (*mapper) .symbolMapper.symbols[i] .q)) if (D < minD) minD = D; min = i; }; for (i=0; i < (*mapper).bitMapper.b; i++) addLLbits(decodedLL, &(*mapper).symbolMapper.code[min [i]); }; /* /* decodeuncoded */ --------------------- INIT/DEALLOCATE FUNCTIONS ---------------------- /* function: demodulatorInitVars Initializes demodulator struct variables. Input Output demodulatorVars* dem - pointer to demodulator struct Modify dem void demodulatorInitVars(demodulatorVars* dem) (*dem).outputS = (linkList*) calloc(1, sizeof(linkList)); llistInitVars((*dem).outputS, 1); /* linked list of real values, not bits */ (*dem).outputP = (linkList*) calloc(, sizeof(linkList)); llistInitVars((*dem).outputP, 1); /* linked list of real values, not bits }/* demodulatorInitVars */ function: freeDemodulatorVars Deallocates memory for demodulator variables. Input demodulatorVars* dem - pointer to demodulator sturct Output Modify *dem void freeDemodulatorVars(demodulatorVars* dem) freeList((*dem).outputP); freeList((*dem).outputS); /* freeDemodulatorVars */ * demod.c ---- OTHER FUNCTIONS -------- function: calcBitLLR This function calculates the log-likehood ratio for a bit in a received symbol. : Input Output Modify twoDSignalPoint point pos int twoDsymbolMapVars symbolMapper double noise var LLR for the specified bit received point - bit to calculate LLR for - symbol mapper struct - channel noise variance - double calcBitLLR(twoDSignalPoint point, int pos, int i, double double double twoDsymbolMapVars symbolMapper, double noise var) half; metricl=0, metricl=O; maxlog = 40; result; half = (int)floor(loglO(symbolMapper.M)/logl1(2) /2); for (i=0; i < symbolMapper.M; i++) if (symbolMapper.code[i] [pos] == 0) /* number of bits per dimension in QAM */ (pos < half) metricO += exp(-1.0/(2*noisevar) * sqr~point.i - symbolMapper.symbols[i].i)); if else metricO += exp(-l.0/(2*noise var) * sqr(point.q - symbolMapper.symbols[i].q)); else (pos < half) metricl += exp(-1.0/(2*noisevar) * sqr(point.i - symbolMapper.symbols[i].i)); if else metricl += exp(-l.O/(2*noise var) * sqr(point.q - symbolMapper.symbols[i}.q)); limit magnitude of LLR so it won't cause computational errors if (fabs(metricl) < exp(-l*maxlog)) metricO = exp(-l * maxlog); if (fabs(metricl) < exp(-l*maxlog)) metricl = exp(-l * max_log); result = log(metricl/metric); if (fabs(result) > maxlog) result = return result; } /* calcBitLLR */ (result/fabs(result)) * maxlog; later on * 183 184 SIMULATION CODE decoder.h file: /* decoder.h type definitions */ typedef struct MAPdecoderVarsStruct int blockSize, numStates, numInputs; double explimit; double ***gamma; /* gamma [k] [dk] [pk] / probA[k] [dk] double **alpha, **beta; /* alpha[k] [m], beta[k] [m] */ char **bitValue; branch** trellis; /* twoDsymbolMapVars* symbolMapper; */ */ linkList* inputS; linkList* inputP; linkList* inputA; linkList* output_Dprob; linkList* output extrinsic; MAPdecoderVars; /* simulation command function */ void doMAPdecoding(MAPdecoderVars* decoder); void doFastRl2MAPdecoding(MAPdecoderVars* decoder); /* init/deallocate functions */ void MAPdecoderInitVars(MAPdecoderVars* decoder, encoderVars* convCode, int blockSize, twoDsymbolMapVars* symbolMapper); void freeMAPDecoderVars(MAPdecoderVars* decoder); /* display/debug functions */ float showErrorPercentage(linkList* origBits, linkList* decodedBits) /* general rate-k/(k+l) RSCC MAP decoder functions */ void calc probS(MAPdecoderVars* decoder); void init probA(MAPdecoderVars* decoder, int factor); void calc gamma(MAPdecoderVars* decoder); void calcalpha(MAPdecoderVars* decoder); void calcbeta(MAPdecoderVars* decoder); void initAB(MAPdecoderVars* decoder, char terminated); void initB usingA(MAPdecoderVars* decoder); void probDecision(linkList *input, linkList* output); /* fast rate-1/2 RSCC MAP decoder functions */ void calcgammaR12(MAPdecoderVars* decoder); void calcalphaRl2(MAPdecoderVars* decoder); void calc_betaRl2(MAPdecoderVars* decoder); void signoecision)1inkList* input, iinkList* output); decoder.c 185 decoder.c /* file: decoder.c This source file contains code for turbo decoding, and the log variants of MAP decoding. MAP decoding, */ #include #include #include #include #include #include #include "general.h" "linkedlist.h" "interleave.h" "conv.h" "mapper.h" "demod.h" "decoder.h" #include <math.h> #include <stdio.h> #include <stdlib.h> ------------------------- /* SIMULATION COMMAND FUNCTION S-------------------------- INIT/DEALLOCATE FUNCTIONS ------ --- ------ -- /* function: MAPdecoderInitVars This function initializes variables used for MAP decoding. Input : MAPdecodervars* decoder encoderVars* convCode int blockSize twoDsymbolMapVars* symbolMapper - pointer to MAP struct - convolutional code to decode - number of k-bit inputs - pointer to symbol mapper struct Output : Modify : *decoder */ void MAPdecoderInitVars(MAPdecoderVars* decoder, encoderVars* convCode, int blockSize, twoDsymbolMapVars* int k, i; /* (*decoder).blockSize = blockSize; (*decoder) .numStates = (int)pow(2, (*convCode) .v); (*decoder) numInputs = (int)pow(2, (*convCode) .k); (*decoder).trellis = (*convCode).trellis; (*decoder).symbolMapper = symbolMapper; */ (*decoder).explimit = 40; (*decoder) .gamma = (double***) calloc (blockSize+l, /* betaEkE [m] beta[0] Em] not used beta[l... blockSize] [m] hold decoding info beta[blockSize] [m] holds initialization info /* alpha~k] [m] alpha[E] [m] hold initialization info alpha[l... blockSize] [m] hold decoding info (*decoder) beta (double**) = calloc (blockSize+1, sizeof (double**)) sizeof (double*)); */ (*decoder) alpha = (double**) calloc (blockSize+1, sizeof (double*)) for (k=O; k <= blockSize; k++) (*decoder) .alpha[k] = (double*) calloc( (*decoder) numStates, sizeof(double)); (*decoder).beta[k = (double*) calloc ((*decoder) .numStates, sizeof (double)); (*decoder) .gamma[k] for (i=; i < = (double") calloc((*decoder) (*decoder) numInputs; i++) il = (double*) numInputs, sizeof (double*)); (*decoder) gamma[k] calloc (2, sizeof (double)); /* assume that all encoders are rate-k/(k+l) -- only 1 parity bit */ /* bitValue[2^k] [k] holds the bit values for an integer for k=2: E0] [ El] [] [2] [ = 00 = = 01 10 symbolMapper) SIMULATION CODE 186 [3] [1 = 11 (*decoder).bitValue = (char**) calloc((*decoder).numInputs, sizeof(char*)); for (i=0; i (*decoder).numInputs; i++) (*decoder).bitValue[i] = (char*) calloc((*convCode).k, sizeof(char)); intToCharArray(i, (*decoder).bitValue[i], (*convCode).k); 1; if (*convCode).k = 1, then it is a rate-1/2 code, and we use doFastRl2decodeBlockO, which uses bit LLR's. Thus, we only need the MAP module output list to have valueLength = 1. /* if (*convCode).k > 1, then it is a rate-k/(k+l) code, and we use doMAPdecoding(), which uses a posteriori probabilities. Thus, we * need the MAP module output list to have valueLength = 2^ ? 1 : (*decoder).numInputs; = ((*convCode).k == 1) (*decoder).output_Dprob = (linkList*) calloc (1, sizeof(linkList)); llistInitVars((*decoder).output_Dprob, k); k (*decoder).output-extrinsic = (linkList*) calloc llistInitVars((*decoder).output_extrinsic, k); /* MAPdecoderInitVars function: (1, sizeof(linkList)); */ freeMAPDecoderVars Deallocates memory for decoder struct variables Input Output Modify MAPdecodervars* decoder *decoder */ void freeMAPDecoderVars(MAPdecoderVars* decoder) int k, i; for (k=O; k < (*decoder).blockSize; k++) free((*decoder).alpha[k]); free((*decoder).beta[k]); for (i=0; i < (*decoder).numInputs; i++) free((*decoder).gamma[k] [i]); free((*decoder).gamma[k]); 1; for (i=0; i < (*decoder).numInputs; i++) free((*decoder).bitValue[i]); free((*decoder).alpha); free((*decoder).beta); free((*decoder).gamma); free((*decoder).bitValue); deleteList((*decoder).output_Dprob); free((*decoder).output Dprob); deleteList((*decoder).outputextrinsic); free((adecoder).ourput extrinsic); } /* /* freeMAPDecoderVars */ ------------------------- DISPLAY/DEBUG FUNCTION ------------------ /* function: showErrorPercentage This function compares the values of two linked lists and calculates the percentage of values that are different. Input Output Modify - list of original bits : linkList* origBits linkList* decodedBits - list of decoded bits % of values that are different */ float showErrorPercentage(linkList* origBits, linkList* decodedBits) float per = 0.0; int i, num; listElement *currEll, *currEl2; num = getNumElements(origBits); currEll = origBits->head; currE12 = decodedBits->head; / decoder.c (i=0; i < num; i++) for if (* (*currEll) value = (*currEl2) per++; currEll = (*currEll).next; currE12 = (*currE12).next; value) }; (per / return /* /* (float)num); showErrorPercentage ---- -------- */ RATE-K/ (K+1) RSCC MAP DECODER FUNCTIONS ------------------ /* function: calc_probS Takes an input linked list with bit LLR's and changes those values to Pr(b=O) = 1/(1 + exp(LLR)), and Pr(b=1) = exp(LLR) / (1 + exp(LLR)) . The LLR value is held in an input element's signal[].i variable. After Pr(b=O) is calculated it is stored in signal[] .i, and Pr(b=l) is stored in signal[] .q LLR is limited to -40 and 40, so probabilities range from 1 / (1 + e^40) = e^(-40) to about 1. Input Output Modify MAPdecoderVars* decoder - pointer to decoder struct *decoder void calc_probS(MAPdecoderVars* decoder) int k, i, num; double expval; listElement* currS; currS = (*decoder).inputS->head; num = getNumElements((*decoder) for (k=0; k , num; k++) for (i=0; i < expval (*currS) (*currS) inputS); (*(*decoder).inputS).valueLength; = exp((*currS).signal[i].i); signal [i].i = 1.0 signal [i].q = exp i++) / (1 + exp val); val * (*currS) .signal [ii.i; }; currS } /* calc_probS function: I I I I I =(*currS).next; */ calcgamma This function calculates part of the gamma parameter used in MAP decoding. gamma][][1 is a three dimensional array. The first index is the time index k, the second index is the input bits integer value i, and the third index is the parity bit p. If Ak is the LLR associated with the input (systematic) bits, and Bk is the LLR for the parity bit, then S(k-1) input i ---------- > S(k) gamma [k] [i] [p] = Pr(Ak) * Pr(Bk) parity p In the actual BCJR algorithm, gamma = Pr(AkI...) Pr(Bkj ...) Pr(dk=i) is "a priori" information supplied by the other MAP module, and is the only probability in gamma that changes with each iteration. Thus, the part of gamma calculated here is the part that does not change between iterations. Input Output Modify : MAPdecoderVars* decoder */ void calcgamma(MAPdecoderVars* decoder) k int k, i, b, bits; Pr(dk=i). / 187 SIMULATION CODE 188 double exp_val; twoDSignalPoint probP; listElement *currElementS, *currElementP; bits = (int) (loglO((*decoder).numInputs)/ioglO(2)); currElementS = (*decoder).inputS->head; currElementP = (*decoder).inputP->head; for (k=l; k <= (*decoder).blockSize; k++) { exp val = exp((*currElementP).signal[0].i); /* pk = 0 */ probP.i = 1.0 / (1 + exp_val); /* pk = 1 */ probP.q = probP.i * exp val; /* ranges from e^(-40) to /* ranges from e^(-40) to 1 */ 1 */ for (i=0; i < (*decoder).numInputs; i++) (*decoder).gamma[k][i] [0] (*decoder).gamma[k] [i] [1] = = probPi; probP.q; for (b=0; b < bits; b++) /* in currElementS, there are valueLength LLR values signal[o] corresponds to the MSB LLR, and signal[valueLenght-1] corresponds to the LSB LLR This ordering is consistent with ordering used by intToCharArray, (*currElementS).signal[b].i corresponds to so bitValue[i] [b] (*decoder).gamma[k][i][0] (*currElementS).signal[b.q; (*decoder).gamma1k][i][1] *= ((*decoder).bitValue[i][b] == 0) ? (*currElementS).signal[b].i d ((* ecoder).bitValue[i][bi == 0) ? (*currElementS).signal[b].i (*currElemenmtS) signal[b].q; ((*decoder).gamma[k][i][01 < (exp(-l* (*decoder).exp_limit) (*decoder).gamma[k] [i] [0] = exp(-l* (*decoder).exp_limit); ((*decoder).gamma[k [i][1} < (exp(-l* (*decoder).exp_ limit))) (*decoder).gamma[k] [i [1] = exp(-l* (*decoder).exp_ limit) if if }; currElementS = currElementP = }; /* /* (*currElementS).next; (*currElementP).next; for k */ calc-gamma s/ function: init_probA This function initializes an empty "a priori" linked list by filling it with equiprobable values if we're decoding rate-k/(k+l), and with O's if we're decoding rate-1/2. The former uses actual "a priori" probabilities, and the latter uses bit log-likehood ratios. Input : MAPdecoderVars* decoder - pointer to decoder struct factor - determines if using "a priori" or LLR int Output Modify : decoder->inputA void init probA(MAPdecoderVars* decoder, int factor) int k, i, num, vlen; twoDSignalPoint* equiprob; listElement* currEl; currEl = (*decoder).inputA->head; equiprob = (twoDSignalPoint*) calloc ((*decoder).numInputs, sizeof(twoDSignalPoint)); for (i=0; i < (*decoder).numInputs; i++) equiprob i].i = 1.0 / (*decoder).numInputs * factor; /* factor is either 0 or 1, depending on if we're using doMAPdecoding) or doFastR12MAPdecoding(). The former uses probabilities (factor = 1) and the latter uses LLR's (factor = 0). num = getNumElements((*decoder).inputS); vlen = (factor == 0) ? 1 : (*decoder).numInputs; (*(*decoder).inputA).valueLength = vlen; for (k=0; k < num; k++) if (currEl == NULL) addLLsignal((*decoder).inputA, equiprob); else for (i=0; i < vlen; i++) decoder.c (*currEl) signal[i] .i = equiprob[i] .i; currEl = currEl->next; }; free(equiprob); 4 /* initprobA */ function: calcalpha This function calculates all the alpha parameters needed in MAP decoding. Input MAPdecoderVars* decoder - pointer to decoder struct Output Modify decoder->alpha */ void calc alpha()MAPdecoderVars* decoder) 7* alpha indices -- alpha[k] [Sk) summation indices */ int k, dk, Sk; int Skprev; /* int pk; double sum; listElement* probA; */ probA = (*decoder).inputA->head; for (k-l; k - (*decoder).blockSize; k++) (Sk = 0; Sk < (*decoder) numStates; Sk++) (*decoder).alphaik] [Sk] = 0; /* now loop through branches: each trellis[Sk-1] [dk] is a branch for (Sk_prev = 0; Skprev < (*decoder) .numStates; Skprev++) for (dk = 0; dk < (*decoder) numlnputs; dk++) for Sk = (*decoder).trellis[Sk_prev] [dk].S2; pk = (*decoder) trellis (*decoder) .alpha[k] [Sk] (*decoder).alpha[k-l1] [Sk prev; }; /* for Sk-prev * /* [Sk prev] [dk].p; += (*decoder) gamma Sk-l -- > (k] [dk] [pk * (*probA) .signal [dk].i * alpha[k] [Sk] is supposed to equal numerator[k] [Sk] / denominator[k] however, because denominator[k] is the same for every state at a time k, it acts like a constant for a time k and divides out in the final calculation of L(dk). However, this means that alpha will accumulate faster with each k, so it'll need to be normalized */ 0; for (Sk=0; Sk < (*decoder) .numStates; sum += for (Sk=0; Sk++) (*decoder).alpha[k][Sk]; Sk a (-decoder) .numStates; Sk++) (*decoder).alpha[k] [Sk] /= sum; if ((*decoder) .alpha[k] [Sk] < exp(-l*(*decoder) explimit)) (*decoder) .alphalk] [Sk] = exp(-l* (*decoder) explimit); /* */ }; probA = } /* for k calcalpha function: (*probA).next; */ */ calc_beta This function calculates all the beta values used in MAP decoding Input Output Modify Sk, /* branch: sum = }; /* */ MAPdecoderVars* decoder - pointer to MAP decoder struct decoder->beta */ void calcbeta()MAPdecoderVars* decoder) int k, Sk; /* beta indices -- beta[k] [Sk] */ int dkfuture, Sk-future; /* summation indices */ int pk; double sum; listElement* probA; input dk */ 189 SIMULATION CODE 190 probA = for (k = (* (*decoder).inputA).tail; ((*decoder).blockSize - 1); k >= 1; k--) f for (Sk = 0; Sk < (*decoder).numStates; Sk++) (*decoder).beta[k] [Sk] = 0; each trellis[Sk-1] [dk] is a branch */ /* loop through branches: for (Sk = 0; Sk < (*decoder).numStates; Sk++) for (dkfuture = 0; dk-future < (*decoder).numInputs; dkfuture++) pk = (*decoder).trellis[Sk] [dk future.p; Skfuture = (*decoder).trellis[Sk]]dkfuture].S2; (*decoder) .beta[k] [Sk] += (*decoder) .gamma[k+1] [dk future] [pk] (*decoder).beta[k+l][Skfuture]; }; /* for dk+l (*probA) .signal dk future] .i */ sum = 0; for (Sk=O; Sk < (*decoder).numStates; Sk++) sum += (*decoder).beta]k]]Sk]; for (Sk=O; Sk < (*decoder).numStates; Sk++) (*decoder).beta[k] [Sk] /= sum; if ((*decoder).beta[k] [Sk < exp(-l*(*decoder).explimit)) (*decoder).beta[k] [Sk] = exp(-l* (*decoder).explimit); }; probA = (*probA).prev; }; /* for k } /* calcbeta */ */ function: doMAPdecoding This function performs MAP decoding. : MAPdecoderVars* decoder - pointer to decoder struct Input Output Modify *decoder void doMAPdecoding(MAPdecoderVars* decoder) { int k, Skprev, Sk, dk; int pk, b, bits; double *lambda; double probS, sum; twoDSignalPoint *prob; listElement *currElementA, *currElementS, *currOutA, *currOutD; lambda = (double*) calloc((*decoder).numInputs, sizeof(double)); prob = (twoDSignalPoint*) calloc((*decoder).numInputs, sizeof(twoDSignalPoint)); /* note: for some reason, bits = (int) (log((*decoder).numInputs) / log(2)); does not give the right number, but bits = (int) (logoO((-decoder).numonputs) / logiu(2)); does. bits = (int) (logl0((*decoder).numInputs) / logiO(2)); calcalpha(decoder); if ((*decoder).beta[(*decoder).blockSize][0] != 1) /* trellis is not terminated */ initB usingA(decoder); calc_beta(decoder); currElementA = (* (*decoder).inputA).head; currElementS = (*(*decoder).inputS).head; currOutD = (* (*decoder).output_Dprob).head; currOutA = (* (*decoder).output_extrinsic).head; for (k=i; k <= (*decoder).blockSize; k++) for (dk=O; dk < (*decoder).numInputs; dk++) lambda[dk] = 0; for (Skprev=O; Sk prev < (*decoder).numStates; Skprev++) for (dk=O; dk < (*decoder).numInputs; dk++) Sk = (*decoder).trellis[Skprev] [dk].S2; pk = (*decoder).trellis[Skprev] [dk].p; lambda[dk] += (*decoder). gamma[k] [dk [pk] 1] [Sk prev] * (*decoder).beta[k] [Sk; }; sum = 0; for (dk=O; dk < (*decoder).numInputs; dk++) sum += lambda[dk]; for (dk=O; dk < (*decoder).numInputs; dk++) * (*currElementA) .signal[dk] .i * (*decoder). alpha[k- decoder.c prob[dk].i = lambda[dk] / sum; /* limit magnitude of probability. prob < 1, so log(prob) < if (fabs(log(prob[dk .i)) > (*decoder) .exp_limit) prob[dk.i = exp(-l (*decoder).explimit); 0 }; if (currOutD == NULL) addLLsignal((*decoder).outputDprob, prob); if currOut is NULL, it will remain NULL throughout this MAP decoding round, and bit probabilities will be added to the */ output list else for (dk=0; dk < (*decoder).numInputs; dk++) (*currOutD).signal[dk].i = prob[dk].i; currOutD = (*currOutD).next; }; for (dk=0; dk < (*decoder).numInputs; dk++) prob[dk].i /= (*currElementA).signal[dk].i; for (dk=0; dk < (*decoder).numInputs; dk++) for (b=0; b < bits; b++) probS = ((*decoder) .bitValue[dk] [b] (*currElementS).signal[b}.q; prob[dkl.i /= probS; == 0) ? (*currElementS).signal[b].i : }; sum = 0; for (dk=; dk < (*decoder).numInputs; dk++) sum += prob[dk].i; for (dk=0; dk < (*decoder).numInputs; dk++) prob[dk].i /= sum; /* limit magnitude of probability. if prob < 1, so log(prob) (fabs(log(prob[dk].i)) > (*decoder).exp_limit) prob[dk].i = exp(-l * (*decoder).explimit); if (currOutA != <0 / NULL) (*currOutA).signal[dk].i = prob[dk].i; if (currOutA == NULL) addLLsignal((*decoder).output extrinsic, prob); else currOutA = (*currOutA).next; currElementA = (*currElementA).next; currElementS = (*currElementS).next; }; free(lambda); free(prob); } /* doMAPdecoding function: */ initAB Initializes alpha and beta parameters used in MAP decoding. - pointer to MAP decoder vars : MAPdecoderVars* decoder char terminated - whether trellis is terminated Input Output Modify decoder.alpha, decoder.beta void initAB(MAPdecoderVars* decoder, char terminated) int m; double equiprob; (*decoder).alpha[0] [0] = 1; for (m = 1; m < (*decoder).numStates; m++) (*decoder).alpha[0] [m] = 0; equiprob = 1.0 / (*decoder).numStates; (*decoder) .beta[(*decoder) .blockSize] [0] for (m = 1; m < = (*decoder).numStates; m++) (terminated) ? 1 : equiprob; (*decoder) .beta[(*decoder) .blockSize] [m] = (terminated) /* initAB */ /* Ifunction: initB usingA ? 0 : equiprob; 191 SIMULATION CODE 192 Initializes beta parameters in the case that the trellis is not terminated. MAPdecoderVars* decoder - pointer to decoder Input Output Modify (*decoder).beta */ void initBusingA(MAPdecoderVars* decoder) int m; for (m=O; m < (*decoder).numStates; m++) (*decoder) .beta[(*decoder) .blockSize] [m] /* = (*decoder) .alpha[(*decoder) .blockSize initB_usingA * /*, function: probDecision Choose maximum a posteriori probability. linkList* input - list of the a posteriori probabilities linkList* output - list of the decoded bits Input Output Modify output list */ void probDecision(linkList* input, linkList* output) int i, numEl, bits, j, max; char b; listElement *currEl, *currOut; /* in this case, valueLength holds the number of array indices, which for output probabilities is already numInputs */ bits = (int) (logl ((*input).valueLength) / log1O(2)); numEl = getNumElements(input); currEl = (*input).head; currOut = (*output).head; for (i=O; i < numEl; i++) max = 0; /* look for highest probability */ for (j /* = 1; j < (*input).valueLength; j++) if ((*currEl).signal[j].i > (*currEl).signal[max].i) max = j; add bits for dk with highest probability to output list * for (j=0; j < bits; j++) b = (max >> if (bits-1 - j)) (currOut == & Oxl; NULL) addLLbits(output, &b); else (*currdut).value[o] = b; currOut = (*currOut).next; }; }; currEl = (*currEl).next; 1; /* /* probDecision / ------------------- FAST RATE-1/2 RSCC MAP DECODER FUNCTIONS ------------------ /* function: doFastRl2MAPdecoding This function performs fast MAP decoding of rate-1/2 RSCC. Input MAPdecoderVars* decoder - pointer to decoder struct Output Modify *decoder void doFastRl2MAPdecoding(MAPdecoderVars* decoder) int k, Skprev, Sk; int pk; */ [m]; decoder.c double lambdal, lambdao; double Ls, La; twoDSignalPoint LLR, Z; const double infinite = le308; double probA[2]; listElement *currElementA, *currElementS, calcalphaRl2(decoder); if ((*decoder) .beta[(*decoder) initB usingA(decoder); calcbetaRi2(decoder); *currOutA, *currOutD; blockSize] [0] != 1) currOutD = (* (*decoder).output_Dprob).head; currOutA = (* (*decoder) outputextrinsic) .head; currElementA = getElement (*decoder) inputA, 1) currElementS = getElement (*decoder) inputS, 1) for (k=l; k <= (*decoder).blockSize; k++) / + exp((*currElementA) probA[0] = 1.0 probA[1] = exp((*currElementA).signal[0].i) (1 [0}.i)); * probA[0]; signal 0; 0; for (Sk_prev=0; Sk_prev < (*decoder) .numStates; Skprev++) { Sk = (*decoder) .trellis[Sk_prev] [0) .S2; pk = (*decoder) trellis [Skprev) [0].p; lambdaO += (*decoder) gamma[k] [0] [pkl * probA[0] * decoder).alpha[k-l] [Sk_prev * (*decoder).beta[k] [Ski; if (lambdaO > infinite) pk = 0; Sk = (*decoder) trellis [Sk_prev] [1}.S2; [Sk_prev] [1].p; pk = (*decoder) trellis lambdal i= (*decoder) gamma[k] [1] [pk] * probA[l] ( decoder).alpha[k-i] [Sk_prcv] * (*decoder).beta[k][Sk]; if (lambdal > infinite) pk = 0; lambdal = lambdaO = }; LLR.i = log(lambdal/lambda0); LLR.q = LLR.i; /* set q = i if in case accidentally use the q part */ (currOutD == NULL) addLLsignal((*decoder) .outputDprob, &LLR); if currOut is NULL, it will remain NULL throughout this MAP decoding round, and bit probabilities will be added to the /* output list */ else (*currOutD).signal(O] = LLR; currOutD = (*currOutD).next; }; La = (*currElementA).signal[0].i; currElementA = (*currElementA).next; Ls = (*currElementS).signal[0].i; currElementS = (*currElementS).next; = Z.i /* (LLR.i - La - Ls); we need to limit the magnitude of the extrinsic LLR, or a priori information. an LLR of explimit or -explimit means that we are VERY certain that the bit is a 1 or 0, respectively. There's no need to further increase or decrease the extrinsic LLR because at this point, we are already certain what the bit is. This is a non-linear operation, but it should not affect the correction power of the turbo code or the performance. */ if (fabs(Z.i) > (*decoder).explimit) Z.i = Z.q = Z.i; if (Z.i)/fabs(Z.i) * (*decoder) .exp limit; (currOutA == NULL) addLLsignal((*decoder) .output extrinsic, &Z); else I (*currOutA).signal[O] = Z; currOutA = (*currOutA).next; }; } /* doFastRl2MAPdecoding function: */ calcgammaR12 Calculates the gamma parameter used in MAP decoding. Input Output Modify MAPdecoderVars* decoder 193 194 SIMULATION CODE void calcgammaR12(MAPdecoderVars* decoder) int k, a, b; double expval; twoDSignalPoint probP, probS; listElement *currElementS, *currElementP; currElementS = (*decoder).inputS->head; currElementP = (*decoder).inputP->head; for (k=1; k <= (*decoder).blockSize; k++) expval = exp((*currElementP).signal[0[.i); probP.i = 1.0 / (1 + expval); /* pk = 0 * /* pk = 1 * probP.q = probP.i * exp_val; expval = exp((*currElementS).signal[0].i); probS.i = 1.0 / (1 + exp_val); probS.q = probS.i * exp_val; (*decoder).gamma[k][0] [0] (*decoder).gamma[k][0][1] (*decoder).gamma[k} [1][0] (*decoder).gamma[k} [1][1] for = = = = probS.i probS.i probS.q probS.q * * * * probP.i; probP.q; probPi; probP.q; (a=0; a<2; a++) for (b=0; b<2; b++) if (* decoder) .gamma[k] [a] [b] (*decoder) gamma[k] [a] [b] < (exp(-1* (*decoder) .explimit))) = exp(-1* (*decoder) .exp-limit); currElementS = (*currElementS).next; currElementP = (*currElementP).next; }; /* for k */ /* calcgammaR12 */ /* function: calc alphaRi2 This function calculates all the alpha parameters needed in MAP decoding. Input Output Modify void calc alphaRl2(MAPdecoderVars* decoder) int k, dk, Sk; int Skprev; int pk; double sum; /* /* alpha indices -- alpha[k] [dk] [Sk] summation indices */ * listElement* currA; double probA[2[; currA = tor (*decoder).inputA->head; k k=1; -= (*decoder).blockSize; k++) probA[0] = 1.0 / (1 + exp((*currA).signal[0].i)); probA[1] = exp((*currA).signal[0[.i) * probA[0]; 1] for (Sk = 0; Sk < (*decoder).numStates; Sk++) (*decoder).alpha[k] [Sk] = 0; /* now loop through branches: each trellis[Sk-1] [dk] is a branch */ for (Skprev = 0; Skprev < (*decoder).numStates; Skprev++) for (dk = 0; dk < (*decoder).numInputs; dk++) { Sk = (*decoder) trellis [Sk_prev] [dk] .S2; /* branch: Sk-1 -- > Sk, input dk */ pk = (*decoder) .trellis [Skjprev] [dkj .p; (*decoder) .alpha [k] [Sk] += (*decoder) .gamma[k] [dk] [pk] * probA[dk] (*decoder) .alpha[k[Skprev; / ; /* for Skprev /* alpha[k] [Sk] is supposed to equal numerator[k] [Sk] / denominator[k] however, because denominator[k] is the same for every state at a time k, it acts like a constant for a time k and divides out in the final calculation of L(dk). However, this means that alpha will accumulate faster with each k, so it'll need to be normalized */ 0; for (Sk=0; Sk < (*decoder).numStates; Sk++) sum += (*decoder).alpha[k[ [Sk]; for (Sk=0; Sk < (*decoder).numStates; Sk++) sum = (*decoder).alpha[k][Sk] /= sum; if ((*decoder) .alpha[k] [Sk] < exp(-l* (*decoder) .exp_limit)) (*decoder) .alpha [k] [Sk] = exp(-1* (*decoder) .exp_limit); /* */ }; decoder.c }; currA = (*currA).next; for k / / S/* calcalphaR12 function: * calc_betaRl2 This function calculates all the beta values used in decoding MAP Input Output Modify void calcbetaR12(MAPdecoderVars* decoder) int k, Sk; /* beta indices -- beta[k][Sk] / int dk future, Sk future; /* summation indices / int pk; double sum; listElement* currA; double probA[2]; currA = (*(*decoder).inputA).tail; for (k = ((*decoder).blockSize - 1); k >= 1; k--) probA[0] = 1.0 / (1 + exp((*currA).signal[O].i)); probAil] = exp((*currA).signal[0].i) * probA[0]; for (Sk = 0; Sk < (*decoder).numStates; Sk++) (*decoder).beta[k][Sk] = 0; /* loop through branches: each trellis[Sk-1] [dk] is a branch * for (Sk = 0; Sk < (*decoder).numStates; Sk++) for (dkfuture = 0; dkfuture < (*decoder).numInputs; dkfuture++) pk = (*decoder) trellis [Sk] [dk_future .p; Skfuture = (*decoder).trellis[Sk) [dk future].S2; (*decoder) beta[k] [Sk] += (*decoder) gamma[k+l] [dk future] [pk (*decoder).beta[k+l][Skfuture]; }; /* for dk+l */ sum = 0; for (Sk=0; Sk < (*decoder).numStates; SUM += (*decoder).beta[k] [Sk]; for (Sk=0; Sk < (*decoder).numStates; Sk++) Sk++) { (*decoder).beta[k] [Sk] /= sum; if ((*decoder) .beta[k] [Sk] < exp(-l* (*decoder) (*decoder) beta [k] [Sk] = exp(-l*(*decoder) }; currA = (*currA).prev; }; * /* for k } /* calc_betaR12 function: */ signDecision Performs a sign decision on LLR's used in MAP decoding to generate the decoded bits. Input Output Modify void signDecision(linkList* input, linkList* output) int i, numEl; char b; listElement *currEl, *currOut; numEl = getNumElements(input); currEl = getElement(input, 1); currOut = (*output).head; for (i=O; b = i a numEl; i++) ((*currEl).signal[0].i >= 0); explimit)) exp-limit); probA[dk future] 195 196 SIMULATION CODE if (currOut == NULL) addLLbits(output, &b); else (*currOut).value[O] = b; currOut = (*currOut).next; }1; currEl = (*currEl).next; }; } /* signoecision */ REFERENCES [1] P. Melsa, "Application of Programmable DSPs for DMT and ADSL", in DSP World Spring Design Conference, April 1998. [2] C. Berrou, A. Glavieux, and P. Thitimajshima, "Near shannon limit error-correcting coding and decoding: Turbo-codes", in Proc. ICC '93, pp. 1064-1070, May 1993. [3] MIT, "Lecture 10: Introduction to Modems", in 6.401/6.450 Introduction to Digital Communications,Fall 2000. [4] P. Robertson, "Illuminating the structure of code and decoder of parallel concatenated recursive systematic (Turbo) codes", in Proc. GLOBECOM '94, pp. 1298-1303, December 1994. [5] Stanford University, "Chapter 11: Code Concatenation and Low-Density Parity Check Codes", in EE379B - Digital CommunicationH1: Coding, Spring 2001. [6] S. Le Goff, A. Glavieux, and C. Berrou, "Turbo-codes and high spectral efficiency modulation", in Proc.ICC '94, pp. 645-649, May 1994. [7] P. Robertson and T. W6rz, "A novel bandwidth efficient coding scheme employing Turbo codes", in Proc.ICC '96, pp. 962-967, June 1996. [8] S. Le Goff and F. 0. Al-Ayyan, "Design of bit-interleaved turbo-coded modulations", Electronic Letters, vol. 37, no. 16, pp. 1030-1031, August 2001. [9] S. Benedetto, D. Divsalar, G. Montorsi, and F. Pollara, "Bandwidth efficient parallel concatenated coding schemes", Electronic Letters, vol. 31, no. 24, pp. 2067-2069, November 1995. [10] S. Benedetto and Guido Montorsi, "Design of parallel concatenated convolutional codes", IEEE Trans. On Communications,vol. 44, no. 5, pp. 591-600, May, 1996. [11] L. R. Bahl, J. Cocke, F. Jelinek, and J. Raviv, "Optimal decoding of linear codes for minimizing symbol error rate", IEEE Trans. On Information Theory, vol. IT-20, pp. 284-287, March 1974. - 197 - 198 REFERENCES [12] P. Robertson and T. W6rz, "Bandwidth efficient Turbo Trellis-coded modulation using punctured component codes", IEEE J. on Selected Areas of Comm., vol. 16, no. 2, February 1998. [13] A. A. Barbalescu and S. S. Pietrobon, "Terminating the trellis of turbo-codes in the same state", Electronic Letters, vol. 31, no. 1, pp. 22-23, January 1995. [14] J. Torres, F. Hirzel, and V. Demjanenko, (VOCAL Technologies Ltd), "G.gen: New proposal of Turbo codes for ADSL modems", ITU standardcontributionBA-020R1 at Antwerp, Belgium, 19-23 June 2000. [15] S. A. Barbulescu, J. A. Torres, F. Hirzel, and V. Demjanenko, (VOCAL Technologies Ltd), "G.gen: G.vdsl: G.dmt.bis: G.lite.bis: : Text to include a Turbo encoder as mandatory in the transmitter for G.922.1.bis and G.992.2.bis following the proposal given in BA-020R1 and HC-037", ITU standardcontribution CF-037 at Clearwater, Florida, 8-12 January 2001. [16] F. Hirzel, J. A. Torres, and V. Demjanenko, (VOCAL Technologies), "G.gen: G.vdsl, G.dmt.bis: G.lite.bis: : Method for using non-square QAM constellations with independent I&Q for Receiver soft-Decision Decoding Techniques", ITU standardcontribution CF-038 at Clearwater, Florida, 8-12 January 2001 [17] W. Farrell, (iCODING Technology), "G.gen : G.dmt.bis : G.lite.bis : Low complexity Turbo coding for the ADSL DMT channel", ITU standard contribution CF-072 at Clearwater, Florida, 8-12 January 2001. [18] D. V. Bruyssel, (Alcatel), "G.gen: G.dmt.bis: G.lite.bis: Multilevel Turbo SCCC code: proposal, performance and complexity", ITU standardcontributionRN-079 at Red Bank, New Jersey, 21-25 May 2001. [19] B. I in and A Deczky, (Catena Networks Inc), "G.gen: G.dmt.bis: (.lite his Proposing 'Multi-level" Turbo TCM concatenated with Reed-Solomon codes as the advanced coding method for G.dmt.bis and G.lite.bis", ITU standardcontribution IC-024 at Irvine, California, 9-13 April 2001. [20] H. Sadjadpour, (AT&T), "G.gen: G.dmt.bis: G.lite.bis: : Encoder structure of Multitone Turbo Trellis coded modulation proposal", ITU standardcontribution RN-027 at Redbank, New Nersey, 21-25 May 2001. [21] H. Sadjadpour, (AT&T), "G.gen: G.dmt.bis: G.lite.bis: : Interleaver design for multi-tone Turbo Trellis coded modulation scheme for G.dmt.bis and G.lite.bis", ITU standardcontributionRN-029 at Redbank, New Jersey, 21-25 May 2001.