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