/* Moving average. Copyright (c) 2007 The Regents of the University of California. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY */ package ptolemy.actor.lib; import ptolemy.data.DoubleToken; import ptolemy.data.IntToken; import ptolemy.data.Token; import ptolemy.data.expr.Parameter; import ptolemy.data.type.BaseType; import ptolemy.kernel.CompositeEntity; import ptolemy.kernel.util.Attribute; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.NameDuplicationException; ////////////////////////////////////////////////////////////////////////// //// MovingAverage /** This actor outputs the moving average of the input sequence. The maximum number of prior inputs to be averaged is given by the <i>maxPastInputsToAverage</i> parameter. The output is the average of the all the previous inputs that have been received if fewer than <i>maxPastInputsToAverage</i> have been received, and the average of the previous <i>maxPastInputsToAverage</i> inputs otherwise. The input can be any data type that supports division by a double. @author Edward A. Lee @version $Id$ @since Ptolemy II 6.1 @Pt.ProposedRating Yellow (eal) @Pt.AcceptedRating Red (eal) @see ptolemy.data.Token */ public class MovingAverage extends Transformer { /** Construct an actor with the given container and name. * @param container The container. * @param name The name of this actor. * @exception IllegalActionException If the actor cannot be contained * by the proposed container. * @exception NameDuplicationException If the container already has an * actor with this name. */ public MovingAverage(CompositeEntity container, String name) throws NameDuplicationException, IllegalActionException { super(container, name); maxPastInputsToAverage = new Parameter(this, "maxPastInputsToAverage"); maxPastInputsToAverage.setTypeEquals(BaseType.INT); maxPastInputsToAverage.setExpression("10"); } /////////////////////////////////////////////////////////////////// //// public variables //// /** The maximum number of past inputs to average. This is an integer * that defaults to 10. */ public Parameter maxPastInputsToAverage; /////////////////////////////////////////////////////////////////// //// public methods //// /** Set a flag that causes recalculation of various local variables * that are used in execution on the next invocation of fire(). * @param attribute The attribute that changed. * @exception IllegalActionException If the attribute contains * an invalid value or if the super method throws it. */ public void attributeChanged(Attribute attribute) throws IllegalActionException { if (attribute == maxPastInputsToAverage) { _maxPastInputsToAverage = ((IntToken) maxPastInputsToAverage .getToken()).intValue(); if (_maxPastInputsToAverage <= 0) { throw new IllegalActionException(this, "Value of maxPastInputsToAverage is required to be positive."); } _reinitializeNeeded = true; } else { super.attributeChanged(attribute); } } // FIXME: State update should occur in postfire. /** Consume the inputs and produce the output. * @exception IllegalActionException If parameter values are invalid, * or if there is no director, or if runtime type conflicts occur. */ public void fire() throws IllegalActionException { super.fire(); // Transfer current input to _data[] if (_mostRecent <= 0) { _mostRecent = _maxPastInputsToAverage - 1; } else { --_mostRecent; } _data[_mostRecent] = input.get(0); if (_count < _maxPastInputsToAverage) { _count++; _factor = new DoubleToken(1.0 / _count); } // Compute the average. Token sum = _data[_mostRecent]; for (int i = 1; i < _count; i++) { int dataIndex = (_mostRecent + i) % (_data.length); sum = sum.add(_data[dataIndex]); } output.send(0, _factor.multiply(sum)); } /** Return false if the input does not have a token. * Otherwise, return what the superclass returns. * @return False if the number of input tokens available is not at least * equal to the decimation parameter. * @exception IllegalActionException If the superclass throws it. */ public boolean prefire() throws IllegalActionException { // If an attribute has changed since the last fire(), or if // this is the first fire(), then reinitialize. if (_reinitializeNeeded) { _reinitialize(); } if (input.hasToken(0)) { return super.prefire(); } else { if (_debugging) { _debug("Called prefire(), which returns false."); } return false; } } /** Perform domain-specific initialization by calling the * initialize(Actor) method of the director. The director may * reject the actor by throwing an exception if the actor is * incompatible with the domain. * Set a flag that reinitializes the data buffer at the first firing. * @exception IllegalActionException If the superclass throws it. */ public void initialize() throws IllegalActionException { super.initialize(); // Must be sure to throw away the old data buffer. _data = null; _reinitializeNeeded = true; } /////////////////////////////////////////////////////////////////// //// protected methods //// /** Reinitialize local variables in response to changes in attributes. * @exception IllegalActionException If there is a problem reinitializing. */ protected void _reinitialize() throws IllegalActionException { // Create new data array and initialize index into it. int length = _maxPastInputsToAverage; _data = new Token[length]; _count = 0; _mostRecent = _maxPastInputsToAverage; _reinitializeNeeded = false; } /////////////////////////////////////////////////////////////////// //// protected variables //// /** The delay line. */ protected Token[] _data; /** The index into the delay line of the most recent input. */ protected int _mostRecent; /** Count of the number of inputs received, bounded by the * size of the _data array. */ protected int _count; /** Maximum number of past inputs to average. */ protected int _maxPastInputsToAverage = 10; /** Indicator that at least an attribute has been changed * since the last initialization. */ protected boolean _reinitializeNeeded = true; /////////////////////////////////////////////////////////////////// //// private variables //// /** The factor by which to multiply the sum of the past inputs. */ private DoubleToken _factor; }