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}