001/* An actor that outputs the discrete integration over successive inputs. 002 003 Copyright (c) 2009-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.domains.de.lib; 029 030import ptolemy.actor.TypedIOPort; 031import ptolemy.actor.util.Time; 032import ptolemy.actor.util.TimedEvent; 033import ptolemy.data.DoubleToken; 034import ptolemy.data.Token; 035import ptolemy.data.expr.Parameter; 036import ptolemy.kernel.CompositeEntity; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.NameDuplicationException; 039import ptolemy.kernel.util.Workspace; 040 041/////////////////////////////////////////////////////////////////// 042//// Integrator 043 044/** Output the discrete integral of the input. Inputs are multiplied by the time 045 * gap from the previous input and accumulated. Output is not generated 046 * until two inputs have been consumed. 047 * <p> 048 * The output type of this actor is forced to be double. 049 * <p> 050 * In postfire(), if an event is present on the <i>reset</i> port, this 051 * actor resets to its initial state, and will not output until two 052 * subsequent inputs have been consumed. This is useful if the input signal is 053 * switched on and off, in which case the time gap between events becomes large 054 * and would otherwise effect the value of the integral. 055 * <p> 056 * The integrator performs linear interpolation between input events, 057 * where the output of the integrator follows the equation 058 * y[n] = y[n-1] + (x[n-1] + x[n])*dt/2 where <i>dt</i> is the time 059 * differential between events. This equates to the trapezoidal method of 060 * approximating a Riemann integral. 061 062 @author Jeff C. Jensen 063 @version $Id$ 064 @since Ptolemy II 8.0 065 @see ptolemy.actor.lib.Accumulator 066 */ 067public class Integrator extends DETransformer { 068 /** Construct an actor with the given container and name. 069 * @param container The container. 070 * @param name The name of this actor. 071 * @exception IllegalActionException If the actor cannot be contained 072 * by the proposed container. 073 * @exception NameDuplicationException If the container already has an 074 * actor with this name. 075 */ 076 public Integrator(CompositeEntity container, String name) 077 throws NameDuplicationException, IllegalActionException { 078 super(container, name); 079 reset = new TypedIOPort(this, "reset", true, false); 080 reset.setMultiport(true); 081 output.setTypeAtLeast(input); 082 output.setWidthEquals(input, false); 083 initialValue = new Parameter(this, "initialValue"); 084 initialValue.setExpression("0.0"); 085 } 086 087 /////////////////////////////////////////////////////////////////// 088 //// public methods //// 089 090 /** Clone the actor into the specified workspace. This calls the 091 * base class and then sets the ports. 092 * @param workspace The workspace for the new object. 093 * @return A new actor. 094 * @exception CloneNotSupportedException If a derived class has 095 * has an attribute that cannot be cloned. 096 */ 097 @Override 098 public Object clone(Workspace workspace) throws CloneNotSupportedException { 099 Integrator newObject = (Integrator) super.clone(workspace); 100 101 newObject.output.setTypeAtLeast(newObject.input); 102 newObject.output.setWidthEquals(newObject.input, false); 103 104 // This is not strictly needed (since it is always recreated 105 // in preinitialize) but it is safer. 106 newObject._lastInput = null; 107 newObject._currentInput = null; 108 newObject._accumulated = null; 109 110 return newObject; 111 } 112 113 /** Consume at most one token from the <i>input</i> port and output 114 * the average of it and the previous input (linear interpolation), 115 * multiplied by the time gap between the two events. 116 * If there has been no previous iteration, no output is sent unless 117 * an initial token has been set. 118 * If there is no input, then produce no output. 119 * @exception IllegalActionException If subtraction or division is not 120 * supported by the supplied tokens. 121 */ 122 @Override 123 public void fire() throws IllegalActionException { 124 super.fire(); 125 if (input.hasToken(0)) { 126 Time currentTime = getDirector().getModelTime(); 127 Token currentToken = input.get(0); 128 _currentInput = new TimedEvent(currentTime, currentToken); 129 130 if (_lastInput != null) { 131 Token lastToken = (Token) _lastInput.contents; 132 Time lastTime = _lastInput.timeStamp; 133 Token timeGap = new DoubleToken( 134 currentTime.subtract(lastTime).getDoubleValue()); 135 Token integrand = new DoubleToken(0.0); 136 137 //Calculate the interpolated value, multiply by dt 138 integrand = currentToken.add(lastToken).multiply(timeGap) 139 .divide(new DoubleToken(2)); 140 141 //Accumulate the integrand 142 if (_accumulated != null) { 143 _accumulated = _accumulated.add(integrand); 144 } else { 145 _accumulated = integrand; 146 } 147 } 148 } 149 150 //If we have accumulated a value, output it here; otherwise, 151 // we did not have an initial value and have not yet received 152 // two inputs. 153 if (_accumulated != null) { 154 output.broadcast(_accumulated); 155 } 156 } 157 158 /** Reset to indicate that no input has yet been seen. 159 * @exception IllegalActionException If the parent class throws it. 160 */ 161 @Override 162 public void initialize() throws IllegalActionException { 163 super.initialize(); 164 _lastInput = null; 165 166 resetAccumulation(); 167 } 168 169 /** Record the most recent input as the latest input. If a reset 170 * event has been received, process it here. 171 * @exception IllegalActionException If the base class throws it. 172 */ 173 @Override 174 public boolean postfire() throws IllegalActionException { 175 //If reset port is connected and has a token, reset state. 176 if (reset.getWidth() > 0) { 177 if (reset.hasToken(0)) { 178 //Consume reset token 179 reset.get(0); 180 181 //Reset the current input 182 _currentInput = null; 183 184 //Reset accumulation 185 resetAccumulation(); 186 } 187 } 188 _lastInput = _currentInput; 189 return super.postfire(); 190 } 191 192 /////////////////////////////////////////////////////////////////// 193 //// protected methods //// 194 195 /** Reset value of the accumulator to either an initial value or null. 196 * @exception IllegalActionException If the base class throws it 197 */ 198 protected void resetAccumulation() throws IllegalActionException { 199 Token initialToken = initialValue.getToken(); 200 201 if (initialToken != null) { 202 _accumulated = initialToken; 203 } else { 204 _accumulated = null; 205 } 206 } 207 208 /////////////////////////////////////////////////////////////////// 209 //// ports and parameters //// 210 211 /** The reset port, which has undeclared type. If this port 212 * receives a token, this actor resets to its initial state, 213 * and no output is generated until two inputs have been received. 214 */ 215 public TypedIOPort reset; 216 217 /** The value produced by the actor on its first iteration. 218 * The default value of this parameter is the double 0.0. 219 */ 220 public Parameter initialValue; 221 222 /////////////////////////////////////////////////////////////////// 223 //// private members //// 224 private TimedEvent _currentInput; 225 226 private TimedEvent _lastInput; 227 228 private Token _accumulated; 229}