001/* Encode an input sequence with a convolutional code.
002
003 Copyright (c) 2003-2015 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.actor.lib.comm;
029
030import ptolemy.actor.lib.Transformer;
031import ptolemy.data.ArrayToken;
032import ptolemy.data.BooleanToken;
033import ptolemy.data.IntToken;
034import ptolemy.data.Token;
035import ptolemy.data.expr.Parameter;
036import ptolemy.data.type.ArrayType;
037import ptolemy.data.type.BaseType;
038import ptolemy.kernel.CompositeEntity;
039import ptolemy.kernel.util.Attribute;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Workspace;
043
044///////////////////////////////////////////////////////////////////
045//// ConvolutionalCoder
046
047/**
048 Encode an input sequence with a convolutional code. The inputs and
049 outputs are booleans.
050
051 <p>The input sequence
052 enters a shift register, and the contents of the shift register are
053 combined using boolean functions given by the <i>polynomialArray</i>
054 parameter. The initial state of the shift register is given by the
055 <i>initialState</i> parameter, which should be a non-negative integer.
056 The <i>uncodedRate</i> parameter, often denoted by <i>k</i> in the
057 coding literature, is the number of bits per firing that are shifted
058 into the shift register. The <i>polynomialArray</i> parameter is an
059 array of positive integers. Each integer indicates one polynomial
060 used for computing output bits. To get a <i>k</i>/<i>n</i>
061 convolutional code, set <i>uncodedRate</i> to <i>k</i> and provide
062 <i>n</i> integers in <i>polynomialArray</i>.</p>
063
064 <p> The integers in <i>polynomialArray</i> are usually most conveniently
065 given as octal numbers. A leading zero indicates an octal
066 number. The <i>i</i>-th bit of the integer indicates whether the
067 <i>i</i>-th tap of the delay line should be used.  All bits that
068 are used are exclusive-ored, thus yielding the parity of the selected
069 bits. See more details in Scrambler actor on using an integer to
070 define a polynomial. The <i>n</i> parity results are produced on
071 the output in a sequence.</p>
072
073 <p> A good convolutional code should have large Hamming distance between
074 any two of its codewords. This is not easily checked, but there are some
075 simple rules that all "good" codes should satisfy:</p>
076 <ol>
077 <li> <i>k</i> should be strictly smaller than <i>n</i>, otherwise
078 the code is not uniquely decodable.  Thus, <i>uncodedRate</i>
079 should be less than the length of <i>polynomialArray</i>.</li>
080 <li> <i>k</i> should not be higher than the highest order of
081 all polynomials, otherwise, some input bits never get
082 involved in computing parities.</li>
083 </ol>
084
085 <p>If these rules are violated, the actor will throw an exception.
086 However, these rules do not guarantee the codeword can be decoded
087 successfully, and it is not always true that larger polynomials
088 yield better codes. Users should check tables for convolutional
089 codes from professional references. For convenience, we list here
090 some convolutional codes that have large distance property.</p>
091 <pre>
092 Rate = 1/2
093 polynomialArray
094 {05, 07}
095 {013, 017}
096 {031, 027}
097 {065, 057}
098 {0155, 0117}
099
100 Rate = 1/3
101 polynomialArray
102 {05, 07, 07}
103 {015, 013, 017}
104 {025, 033, 037}
105 {071, 065, 057}
106 {0155, 0123, 0137}
107
108 Rate = 1/4
109 polynomialArray
110 {05, 07, 07, 07}
111 {015, 013, 013, 017}
112 {025, 035, 033, 037}
113 {065, 073, 047, 057}
114 {0135, 0135, 0163, 0147}
115
116 Rate = 1/5
117 polynomialArray
118 {07, 07, 07, 05, 05}
119 {017, 017, 015, 013, 013}
120 {037, 035, 033, 025, 027}
121 {057, 047, 067, 053, 075}
122
123 Rate = 1/6
124 polynomialArray
125 {07, 07, 07, 07, 05, 05}
126 {017, 017, 015, 015, 013, 013}
127 {037, 027, 035, 033, 025, 027}
128 {067, 057, 055, 053, 071, 075}
129
130 Rate = 2/3
131 polynomialArray
132 {017, 06, 013}
133 {072, 057, 027}
134 {0171, 0166, 0273}
135
136 Rate = k/5
137 k     polynomialArray
138 2     {017, 016, 011, 05, 02}
139 2     {072, 047, 025, 053, 075}
140 3     {056, 062, 057, 043, 071}
141
142 Rate = k/7
143 k     polynomialArray
144 2     {012, 06, 05, 013, 013, 015, 017}
145 2     {066, 055, 027, 071, 052, 056, 057}
146 3     {051, 042, 036, 023, 075, 061, 047}
147
148 Rate  polynomialArray
149 3/4   {064, 052, 043, 071}
150 3/8   {054, 021, 062, 043, 045, 036, 057, 071}
151 </pre>
152
153 <p> Note that this implementation is limited to a shift register
154 length of 32 because of the specification of the polynomials and
155 initial shift register state as 32-bit integers.</p>
156
157 <p>For more information on convolutional codes, see Proakis, Digital
158 Communications, Fourth Edition, McGraw-Hill, 2001, pp. 471-477,
159 or Barry, Lee and Messerschmitt, <i>Digital Communication</i>, Third Edition,
160 Kluwer, 2004.</p>
161
162 @author Ye Zhou, contributor: Edward A. Lee
163 @version $Id$
164 @since Ptolemy II 3.0
165 @Pt.ProposedRating Yellow (eal)
166 @Pt.AcceptedRating Red (cxh)
167 @see Scrambler
168 @see ViterbiDecoder
169 */
170public class ConvolutionalCoder extends Transformer {
171    /** Construct an actor with the given container and name.
172     *  The output and trigger ports are also constructed.
173     *  @param container The container.
174     *  @param name The name of this actor.
175     *  @exception IllegalActionException If the entity cannot be contained
176     *   by the proposed container.
177     *  @exception NameDuplicationException If the container already has an
178     *   actor with this name.
179     */
180    public ConvolutionalCoder(CompositeEntity container, String name)
181            throws NameDuplicationException, IllegalActionException {
182        super(container, name);
183
184        uncodedRate = new Parameter(this, "uncodedRate");
185        uncodedRate.setTypeEquals(BaseType.INT);
186        uncodedRate.setExpression("1");
187
188        polynomialArray = new Parameter(this, "polynomialArray");
189        polynomialArray.setTypeEquals(new ArrayType(BaseType.INT));
190        polynomialArray.setExpression("{05, 07}");
191
192        initialState = new Parameter(this, "initialState");
193        initialState.setTypeEquals(BaseType.INT);
194        initialState.setExpression("0");
195
196        // Declare data types, consumption rate and production rate.
197        input.setTypeEquals(BaseType.BOOLEAN);
198        _inputRate = new Parameter(input, "tokenConsumptionRate");
199        _inputRate.setExpression("1");
200        output.setTypeEquals(BaseType.BOOLEAN);
201        _outputRate = new Parameter(output, "tokenProductionRate");
202        _outputRate.setExpression("1");
203    }
204
205    ///////////////////////////////////////////////////////////////////
206    ////                     ports and parameters                  ////
207
208    /** An array of integers defining an array of polynomials with
209     *  binary coefficients. The coefficients indicate the presence (1)
210     *  or absence (0) of a tap in the shift register. Each element
211     *  of this array parameter should be a positive integer.
212     *  The default value is {05, 07}.
213     */
214    public Parameter polynomialArray;
215
216    /** Integer defining the initial state of the shift register.
217     *  The i-th bit of the integer indicates the value of the
218     *  i-th register. This parameter should be a non-negative
219     *  integer. Its default value is the integer 0.
220     */
221    public Parameter initialState;
222
223    /** Integer defining the number of bits that the shift register
224     *  takes in each firing. It should be a positive integer. Its
225     *  default value is the integer 1.
226     */
227    public Parameter uncodedRate;
228
229    ///////////////////////////////////////////////////////////////////
230    ////                         public methods                    ////
231
232    /** If the attribute being changed is <i>uncodedRate</i>,
233     *  then verify that it is a positive integer; if it is
234     *  <i>polynomialArray</i>, then verify that each of its elements is
235     *  a positive integer and find the maximum value among them, which
236     *  is used to compute the highest order among all polynomials.
237     *  @param attribute The attribute that changed.
238     *  @exception IllegalActionException If <i>uncodedRate</i> is
239     *  non-positive or any element of <i>polynomialArray</i> is non-positive.
240     */
241    @Override
242    public void attributeChanged(Attribute attribute)
243            throws IllegalActionException {
244        if (attribute == uncodedRate) {
245            _inputNumber = ((IntToken) uncodedRate.getToken()).intValue();
246
247            if (_inputNumber < 1) {
248                throw new IllegalActionException(this,
249                        "inputLength must be non-negative.");
250            }
251
252            // Set a flag indicating the private variable
253            // _inputNumber is invalid, but do not compute
254            // the value until all parameters have been set.
255            _inputNumberInvalid = true;
256
257            // Set the input consumption rate.
258            _inputRate.setToken(new IntToken(_inputNumber));
259        } else if (attribute == polynomialArray) {
260            ArrayToken maskToken = (ArrayToken) polynomialArray.getToken();
261            _maskNumber = maskToken.length();
262            _mask = new int[_maskNumber];
263            _maxPolyValue = 0;
264
265            for (int i = 0; i < _maskNumber; i++) {
266                _mask[i] = ((IntToken) maskToken.getElement(i)).intValue();
267
268                if (_mask[i] <= 0) {
269                    throw new IllegalActionException(this,
270                            "Polynomial is required to be strictly positive.");
271                }
272
273                if (_mask[i] > _maxPolyValue) {
274                    _maxPolyValue = _mask[i];
275                }
276            }
277
278            _inputNumberInvalid = true;
279
280            // Set the output production rate.
281            _outputRate.setToken(new IntToken(_maskNumber));
282        } else {
283            super.attributeChanged(attribute);
284        }
285    }
286
287    /** Clone the actor into the specified workspace.
288     *  @param workspace The workspace for the new object.
289     *  @return A new actor.
290     *  @exception CloneNotSupportedException If a derived class contains
291     *   an attribute that cannot be cloned.
292     */
293    @Override
294    public Object clone(Workspace workspace) throws CloneNotSupportedException {
295        ConvolutionalCoder newObject = (ConvolutionalCoder) super.clone(
296                workspace);
297
298        newObject._inputRate = (Parameter) newObject.input
299                .getAttribute("tokenConsumptionRate");
300        newObject._mask = new int[newObject._maskNumber];
301        newObject._outputRate = (Parameter) newObject.output
302                .getAttribute("tokenProductionRate");
303
304        return newObject;
305    }
306
307    /** Read <i>uncodedRate</i> bits from the input port and shift
308     *  them into the shift register. Compute the parity for each
309     *  polynomial specified in <i>polynomialArray</i>. Send the results
310     *  in sequence to the output. The i-th bit in the output
311     *  corresponds to the parity computed using the i-th polynomial.
312     */
313    @Override
314    public void fire() throws IllegalActionException {
315        super.fire();
316        if (_inputNumberInvalid) {
317            if (_inputNumber >= _maskNumber) {
318                throw new IllegalActionException(this,
319                        "Output rate should be larger than input rate.");
320            }
321
322            if (1 << _inputNumber > _maxPolyValue) {
323                throw new IllegalActionException(this,
324                        "The highest order of all polynomials is too low.");
325            }
326
327            _inputNumberInvalid = false;
328        }
329
330        _latestShiftReg = _shiftReg;
331
332        // Read from the input port and shift the bits into
333        // the shift register.
334        Token[] inputToken = input.get(0, _inputNumber);
335        int reg = _latestShiftReg;
336
337        for (int i = 0; i < _inputNumber; i++) {
338            reg = reg << 1;
339
340            BooleanToken input = (BooleanToken) inputToken[i];
341            reg = reg | (input.booleanValue() ? 1 : 0);
342        }
343
344        _latestShiftReg = reg;
345
346        // Compute the parities for all polynomials respectively.
347        BooleanToken[] result = new BooleanToken[_maskNumber];
348        int[] parity; /*= new int[_maskNumber];*/
349        parity = _calculateParity(_mask, _maskNumber, reg);
350
351        // Send the parity results to the output.
352        for (int i = 0; i < _maskNumber; i++) {
353            if (parity[i] == 1) {
354                result[i] = BooleanToken.TRUE;
355            } else {
356                result[i] = BooleanToken.FALSE;
357            }
358        }
359
360        output.broadcast(result, result.length);
361    }
362
363    /** Initialize the actor by resetting the shift register state
364     *  equal to the value of <i>initialState</i>.
365     *  @exception IllegalActionException If the parent class throws it.
366     */
367    @Override
368    public void initialize() throws IllegalActionException {
369        super.initialize();
370        _latestShiftReg = _shiftReg = ((IntToken) initialState.getToken())
371                .intValue();
372    }
373
374    /** Record the most recent shift register state as the new
375     *  state for the next iteration.
376     *  @exception IllegalActionException If the base class throws it
377     */
378    @Override
379    public boolean postfire() throws IllegalActionException {
380        _shiftReg = _latestShiftReg;
381        return super.postfire();
382    }
383
384    ///////////////////////////////////////////////////////////////////
385    ////                         private methods                   ////
386
387    /** Calculate the parities given by the polynomial array
388     *  and the state of shift register.
389     *  @param mask The polynomial array.
390     *  @param reg State of shift register.
391     *  @return Parities stored in an array.
392     */
393    private int[] _calculateParity(int[] mask, int maskNumber, int reg) {
394        int[] parity = new int[maskNumber];
395
396        for (int i = 0; i < maskNumber; i++) {
397            int masked = mask[i] & reg;
398
399            // Find the parity of the "masked".
400            parity[i] = 0;
401
402            // Calculate the parity of the masked word.
403            while (masked > 0) {
404                parity[i] = parity[i] ^ masked & 1;
405                masked = masked >> 1;
406            }
407        }
408
409        return parity;
410    }
411
412    ///////////////////////////////////////////////////////////////////
413    ////                         private variables                 ////
414    // Consumption rate of the input port.
415    private Parameter _inputRate;
416
417    // Production rate of the output port.
418    private Parameter _outputRate;
419
420    // Record the state of the shift register.
421    private int _shiftReg;
422
423    // Updated state of the shift register.
424    private int _latestShiftReg;
425
426    // Number of bits the actor consumes per firing.
427    private int _inputNumber;
428
429    // Polynomial array.
430    private int[] _mask;
431
432    // Number of polynomials.
433    private int _maskNumber;
434
435    // The maximum value in integer among all polynomials.
436    private int _maxPolyValue;
437
438    // A flag indicating that the private variable
439    // _inputNumber is invalid.
440    private transient boolean _inputNumberInvalid = true;
441}