001/* Hamming Coder.
002
003 Copyright (c) 2003-2014 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.BooleanToken;
032import ptolemy.data.IntToken;
033import ptolemy.data.Token;
034import ptolemy.data.expr.Parameter;
035import ptolemy.data.type.BaseType;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.Attribute;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.NameDuplicationException;
040import ptolemy.kernel.util.Workspace;
041
042///////////////////////////////////////////////////////////////////
043//// HammingCoder
044
045/**
046 Encode the information symbols into Hamming code.
047 Let <i>k</i> denotes parameter <i>uncodedRate</i> and <i>n</i> denotes
048 parameter <i>codedRate</i>. During each firing, the actor consumes
049 <i>k</i> bits and encode them into a block of code with length <i>n</i>.
050 The rate of the code is <i>k/n</i>.
051 <p>
052 For a Hamming code, <i>k</i> and <i>n</i> must satisfy the following:
053 <i>n</i> = 2<i><sup>m</sup></i> - 1
054 <i>k</i> = 2<sup><i>m</i></sup> - 1 - <i>m</i>;
055 where <i>m</i> is any positive integer. Note <i>m</i> = <i>n</i> - <i>k</i>.
056 It is called the order of the Hamming code. The lowest order is <i>m</i> = 2,
057 and (<i>n</i>, <i>k</i>) = (3, 1).
058 <p>
059 The generator matrix G is defined as:
060 G<i><sub>k*n</sub></i> = [I<i><sub>k</sub></i> | P<i><sub>k*(n-k)</sub></i> ]
061 where P is called the parity matrix.
062 The subscript of a matrix indicates its dimension.
063 <p>
064 The parity check matrix H is defined as:
065 H<sub><i>(n-k)*n</i></sub> = [P<sup>T</sup> | I<sub><i>n-k</i></sub> ]
066 Each column of H must be one of the non-zero <i>n</i> = 2<sup><i>m</i></sup> - 1
067 combinations of <i>m</i> bits.
068 <p>
069 To generate a Hamming code, the <i>k</i> information bits is considered
070 as a row vector <i><u>X</u></i>. Its Hamming code is
071 <i><u>Y</u></i> = <i><u>X</u></i> * G.
072 Hence <i><u>Y</u></i> is a row vector of length <i>n</i>. The result is
073 then sent to the output port in sequence.
074 <p>
075 For more information on Hamming codes, see Proakis, Digital
076 Communications, Fourth Edition, McGraw-Hill, 2001, pp. 416-424.
077 <p>
078 @author Ye Zhou
079 @version $Id$
080 @since Ptolemy II 3.0
081 @Pt.ProposedRating Red (eal)
082 @Pt.AcceptedRating Red (cxh)
083 */
084public class HammingCoder extends Transformer {
085    /** Construct an actor with the given container and name.
086     *  The output and trigger ports are also constructed.
087     *  @param container The container.
088     *  @param name The name of this actor.
089     *  @exception IllegalActionException If the entity cannot be contained
090     *   by the proposed container.
091     *  @exception NameDuplicationException If the container already has an
092     *   actor with this name.
093     */
094    public HammingCoder(CompositeEntity container, String name)
095            throws NameDuplicationException, IllegalActionException {
096        super(container, name);
097
098        uncodedRate = new Parameter(this, "uncodedRate");
099        uncodedRate.setTypeEquals(BaseType.INT);
100        uncodedRate.setExpression("4");
101
102        codedRate = new Parameter(this, "codedRate");
103        codedRate.setTypeEquals(BaseType.INT);
104        codedRate.setExpression("7");
105
106        // Declare data types, consumption rate and production rate.
107        input.setTypeEquals(BaseType.BOOLEAN);
108        _inputRate = new Parameter(input, "tokenConsumptionRate",
109                new IntToken(1));
110        output.setTypeEquals(BaseType.BOOLEAN);
111        _outputRate = new Parameter(output, "tokenProductionRate",
112                new IntToken(1));
113    }
114
115    ///////////////////////////////////////////////////////////////////
116    ////                     ports and parameters                  ////
117
118    /** Integer defining the uncode block size. It should be a positive
119     *  integer. Its default value is the integer 4.
120     */
121    public Parameter uncodedRate;
122
123    /** Integer defining the Hamming code block size.
124     *  This parameter should be a non-negative integer.
125     *  Its default value is the integer 7.
126     */
127    public Parameter codedRate;
128
129    ///////////////////////////////////////////////////////////////////
130    ////                         public methods                    ////
131
132    /** If the attribute being changed is <i>uncodedRate</i> or
133     *  <i>uncodedRate</i>, then verify that it is a positive integer.
134     *  Set the tokenConsumptionRate and tokenProductionRate.
135     *  @param attribute The attribute that changed.
136     *  @exception IllegalActionException If <i>codedRate</i>
137     *  or <i>uncodedRate</i> is not positive.
138     */
139    @Override
140    public void attributeChanged(Attribute attribute)
141            throws IllegalActionException {
142        if (attribute == codedRate) {
143            _codeSizeValue = ((IntToken) codedRate.getToken()).intValue();
144
145            if (_codeSizeValue <= 0) {
146                throw new IllegalActionException(this,
147                        "codedRate must be positive.");
148            }
149
150            // set the output production rate.
151            _outputRate.setToken(new IntToken(_codeSizeValue));
152        } else if (attribute == uncodedRate) {
153            _uncodeSizeValue = ((IntToken) uncodedRate.getToken()).intValue();
154
155            if (_uncodeSizeValue < 1) {
156                throw new IllegalActionException(this,
157                        "uncodedRate must be non-negative.");
158            }
159
160            // Set a flag indicating the private variables
161            // _uncodeSizeValue and/or _codeSizeValue is invalid,
162            // but do not compute the value until all parameters
163            // have been set.
164            _parameterInvalid = true;
165
166            // Set the input consumption rate.
167            _inputRate.setToken(new IntToken(_uncodeSizeValue));
168        } else {
169            super.attributeChanged(attribute);
170        }
171    }
172
173    /** Clone the actor into the specified workspace.
174     *  @param workspace The workspace for the new object.
175     *  @return A new actor.
176     *  @exception CloneNotSupportedException If a derived class contains
177     *   an attribute that cannot be cloned.
178     */
179    @Override
180    public Object clone(Workspace workspace) throws CloneNotSupportedException {
181        HammingCoder newObject = (HammingCoder) super.clone(workspace);
182
183        newObject._inputRate = (Parameter) newObject.input
184                .getAttribute("tokenConsumptionRate");
185        newObject._outputRate = (Parameter) newObject.output
186                .getAttribute("tokenProductionRate");
187        return newObject;
188    }
189
190    /** If the attributes has changed, check the validity of
191     *  uncodedRate and codedRate. Generate the parity matrix.
192     *  Read "uncodedRate" number of tokens from the input port
193     *  and compute the parities. Send the parities in sequence to the
194     *  output port.
195     */
196    @Override
197    public void fire() throws IllegalActionException {
198        super.fire();
199        if (_parameterInvalid) {
200            if (_uncodeSizeValue >= _codeSizeValue) {
201                throw new IllegalActionException(this,
202                        "codedRate must be greater than uncodedRate.");
203            }
204
205            _order = _codeSizeValue - _uncodeSizeValue;
206
207            if (_codeSizeValue != (1 << _order) - 1) {
208                throw new IllegalActionException(this,
209                        "Invalid pair of uncodedRate and codedRate.");
210            }
211
212            // Generate P.
213            _parityMatrix = new int[_uncodeSizeValue][_order];
214
215            int flag = 0;
216            int index = 0;
217
218            for (int i = 1; i <= _codeSizeValue; i++) {
219                if (i == 1 << flag) {
220                    flag++;
221                } else {
222                    for (int j = 0; j < _order; j++) {
223                        _parityMatrix[index][j] = i >> _order - j - 1 & 1;
224                    }
225
226                    index++;
227                }
228            }
229
230            _parameterInvalid = false;
231        }
232
233        // Read from the input; set up output size.
234        Token[] inputToken = input.get(0, _uncodeSizeValue);
235        BooleanToken[] result = new BooleanToken[_codeSizeValue];
236
237        for (int i = 0; i < _uncodeSizeValue; i++) {
238            result[i] = (BooleanToken) inputToken[i];
239        }
240
241        // Compute parities.
242        int[] parity = new int[_order];
243
244        // Initialize.
245        for (int i = 0; i < _order; i++) {
246            parity[i] = 0;
247        }
248
249        for (int i = 0; i < _uncodeSizeValue; i++) {
250            for (int j = 0; j < _order; j++) {
251                parity[j] = parity[j] ^ (result[i].booleanValue() ? 1 : 0)
252                        & _parityMatrix[i][j];
253            }
254        }
255
256        // Send the parity results to the output.
257        for (int i = 0; i < _order; i++) {
258            result[i + _uncodeSizeValue] = new BooleanToken(parity[i] == 1);
259        }
260
261        output.broadcast(result, result.length);
262    }
263
264    ///////////////////////////////////////////////////////////////////
265    ////                         private variables                 ////
266    // Consumption rate of the input port.
267    private Parameter _inputRate;
268
269    // Production rate of the output port.
270    private Parameter _outputRate;
271
272    // Uncode block length.
273    private int _uncodeSizeValue;
274
275    // Hamming codeword length.
276    private int _codeSizeValue;
277
278    // Order of the Hamming code.
279    private int _order;
280
281    // matrix "P" for this Hamming code.
282    private int[][] _parityMatrix;
283
284    // A flag indicating that the private variable
285    // _inputNumber is invalid.
286    private transient boolean _parameterInvalid = true;
287}