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}