001/* Produce a Hadamard codeword by selecting a row from a Hadamard matrix. 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.Source; 031import ptolemy.actor.parameters.PortParameter; 032import ptolemy.data.BooleanToken; 033import ptolemy.data.IntToken; 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; 040 041/////////////////////////////////////////////////////////////////// 042//// HadamardCode 043 044/** 045 Produce a Hadamard codeword by selecting a row from a Hadamard matrix. 046 The log base 2 of the matrix dimension is given by the <i>log2Length</i> 047 parameter, which should be a non-negative integer smaller than 32. 048 The row index is given by the <i>index</i> parameter or by the associated 049 <i>index</i> port, which should be a non-negative integer smaller 050 than the matrix dimension. If the index changes value when the actor is 051 in the middle of producing a sequence of Hadamard codeword, the actor 052 will take on the new index value, and start to produce the new codeword 053 from the beginning. 054 <p> 055 A Hadamard matrix is defined in the following way: 056 <p> 057 <i>H</i><sub>1</sub> = [1, 1; 1, -1] 058 <p> 059 <i>H</i><sub><i>n</i>+1</sub> = [<i>H</i><sub><i>n</i></sub>, 060 <i>H</i><sub><i>n</i></sub>; 061 <i>H</i><sub><i>n</i></sub>, -<i>H</i><sub><i>n</i></sub>] 062 <p> 063 where <i>n</i> is a positive integer. 064 Therefore, H<sub><i>n</i></sub> is a 2<sup><i>n</i></sup> by 065 2<sup><i>n</i></sup> square matrix. 066 The codeword length is 2<sup><i>n</i></sup>. 067 <p> 068 The actor produces Hadamard codeword in booleans. Therefore, 1 is treated 069 as "true" and -1 is treated as "false". 070 <p> 071 @author Edward A. Lee and Ye Zhou 072 @version $Id$ 073 @since Ptolemy II 3.0 074 @Pt.ProposedRating Red (eal) 075 @Pt.AcceptedRating Red (cxh) 076 */ 077public class HadamardCode extends Source { 078 /** Construct an actor with the given container and name. 079 * The output and trigger ports are also constructed. 080 * @param container The container. 081 * @param name The name of this actor. 082 * @exception IllegalActionException If the entity cannot be contained 083 * by the proposed container. 084 * @exception NameDuplicationException If the container already has an 085 * actor with this name. 086 */ 087 public HadamardCode(CompositeEntity container, String name) 088 throws NameDuplicationException, IllegalActionException { 089 super(container, name); 090 091 index = new PortParameter(this, "index"); 092 index.setTypeEquals(BaseType.INT); 093 index.setExpression("0"); 094 095 log2Length = new Parameter(this, "log2Length"); 096 log2Length.setTypeEquals(BaseType.INT); 097 log2Length.setExpression("5"); 098 099 // Declare output data type. 100 output.setTypeEquals(BaseType.BOOLEAN); 101 } 102 103 /////////////////////////////////////////////////////////////////// 104 //// ports and parameters //// 105 106 /** Index of the code to generate. Codes with different indexes 107 * are orthogonal. This is an int with default 0. It should 108 * not exceed length-1, where length = 2^log2Length. 109 */ 110 public PortParameter index; 111 112 /** Log base 2 of the length of the code. This is an integer with 113 * default 5. It is required to be greater than 0. 114 */ 115 public Parameter log2Length; 116 117 /////////////////////////////////////////////////////////////////// 118 //// public methods //// 119 120 /** If the attribute being changed is <i>log2Length</i>, then 121 * calculate the new Hadamard sequence; if it is <i>index</i>, 122 * then verify that is non-negative and check if it is different 123 * from the previous value. 124 * @param attribute The attribute that changed. 125 * @exception IllegalActionException If <i>index</i> is negative 126 * or <i>log2Length</i> is not strictly positive. 127 */ 128 @Override 129 public void attributeChanged(Attribute attribute) 130 throws IllegalActionException { 131 if (attribute == index) { 132 int indexValue = ((IntToken) index.getToken()).intValue(); 133 134 if (indexValue < 0) { 135 throw new IllegalActionException(this, 136 "index parameter is not permitted to be negative."); 137 } 138 139 // Set a flag indicating that the private variable _row 140 // is invalid, but don't recompute the value until all 141 // parameters have been set. 142 if (indexValue != _previousIndex) { 143 _rowValueInvalid = true; 144 } 145 } else if (attribute == log2Length) { 146 int log2LengthValue = ((IntToken) log2Length.getToken()).intValue(); 147 148 if (log2LengthValue <= 0) { 149 throw new IllegalActionException(this, 150 "log2Length parameter is required to be " 151 + "strictly positive."); 152 } 153 154 // Assuming an int is 32 bits, our implementation will only 155 // work if this is less than 32. 156 if (log2LengthValue >= 32) { 157 throw new IllegalActionException(this, 158 "log2Length parameter is required to be " 159 + "less than 32."); 160 } 161 162 // Set a flag indicating that the private variable _row 163 // is invalid, but don't recompute the value until all 164 // parameters have been set. 165 _rowValueInvalid = true; 166 } else { 167 super.attributeChanged(attribute); 168 } 169 } 170 171 /** Read from the associated <i>index</i> port if there is any input. 172 * The actor compares the new index value with the old one. 173 * If the value changes, the actor will interrupt the current 174 * output sequence, compute the new Hadamard codeword, and send it 175 * to the output in sequence. 176 * If the index remains constant when it reaches the end of a 177 * Hadamard codeword sequence, the next iteration will restart 178 * from the beginning of that codeword. 179 * @exception IllegalActionException If <i>index</i> is out of range. 180 */ 181 @Override 182 public void fire() throws IllegalActionException { 183 super.fire(); 184 index.update(); 185 186 if (_rowValueInvalid) { 187 _latestIndex = ((IntToken) index.getToken()).intValue(); 188 189 int log2LengthValue = ((IntToken) log2Length.getToken()).intValue(); 190 191 // Power of two calculated using a shift. 192 int matrixDimension = 1 << log2LengthValue; 193 194 if (_latestIndex >= matrixDimension) { 195 throw new IllegalActionException(this, 196 "index is out of range."); 197 } 198 199 _row = _calculateRow(matrixDimension, _latestIndex); 200 _rowValueInvalid = false; 201 202 // Reset the index to start at the beginning of the 203 // new sequence. 204 _index = 0; 205 } 206 207 output.broadcast(new BooleanToken(_row[_index])); 208 209 _index++; 210 211 if (_index >= _row.length) { 212 _index = 0; 213 } 214 } 215 216 /** Initialize the actor by resetting the index counter to begin 217 * at the beginning of the Hadamard sequence. 218 * @exception IllegalActionException If there is no director. 219 */ 220 @Override 221 public void initialize() throws IllegalActionException { 222 super.initialize(); 223 224 // Since the actor should always compute the Hadamard 225 // sequence when it fires for the first time, the _previousIndex 226 // is set to a value that _latestIndex can never take. 227 // Thus the computation can be carried out. 228 _previousIndex = -1; 229 _index = 0; 230 } 231 232 @Override 233 public boolean postfire() throws IllegalActionException { 234 _previousIndex = _latestIndex; 235 return super.postfire(); 236 } 237 238 /////////////////////////////////////////////////////////////////// 239 //// private methods //// 240 241 /** Calculate Hardmard row given by the Hadamard matrix dimension 242 * and the row index. The method computes iteratively by degrading 243 * the matrix dimension into half, until it reaches H<sub>1</sub>. 244 * @param matrixDimension Hadamard matrix dimension. 245 * @param index Row index. 246 * @return Desired Hadamard row. 247 */ 248 private boolean[] _calculateRow(int matrixDimension, int index) { 249 // NOTE: Don't need to check the arguments for validity 250 // because this is a private method, and the usage pattern 251 // guarantees that matrixDimension is a power of 2 and that 252 // index is in range. 253 // NOTE: use <= in case a bug somewhere results in this 254 // dropping to one or zero. Shouldn't happen. In theory, 255 // == is sufficient. However, such a bug would lead to 256 // an infinite recursion and stack overflow, which is a 257 // particularly nasty error. 258 if (matrixDimension <= 2) { 259 if (index == 0) { 260 return _row0; 261 } else { 262 return _row1; 263 } 264 } else { 265 boolean[] result = new boolean[matrixDimension]; 266 int halfDimension = matrixDimension / 2; 267 int indexIntoHalfMatrix = index; 268 269 if (index >= halfDimension) { 270 indexIntoHalfMatrix -= halfDimension; 271 } 272 273 boolean[] halfRow = _calculateRow(halfDimension, 274 indexIntoHalfMatrix); 275 System.arraycopy(halfRow, 0, result, 0, halfDimension); 276 277 if (index >= halfDimension) { 278 for (int i = 0; i < halfDimension; i++) { 279 result[halfDimension + i] = !halfRow[i]; 280 } 281 } else { 282 System.arraycopy(halfRow, 0, result, halfDimension, 283 halfDimension); 284 } 285 286 return result; 287 } 288 } 289 290 /////////////////////////////////////////////////////////////////// 291 //// private variable //// 292 // Index of the element in the Hadamard row. 293 private int _index; 294 295 // The previous index value from the input port. 296 private int _previousIndex; 297 298 // The current index value from the input port. 299 private int _latestIndex; 300 301 // Hadamard row computed from _calculateRow. 302 private boolean[] _row; 303 304 // Rows of H<sub>1</sub>. 305 private static boolean[] _row0 = { true, true }; 306 307 private static boolean[] _row1 = { true, false }; 308 309 // A flag indicating that the private variable _row is invalid. 310 private transient boolean _rowValueInvalid = true; 311}