001/* An actor that outputs the discrete derivative between successive inputs. 002 003 Copyright (c) 1998-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.type.BaseType; 035import ptolemy.kernel.CompositeEntity; 036import ptolemy.kernel.util.IllegalActionException; 037import ptolemy.kernel.util.NameDuplicationException; 038import ptolemy.kernel.util.Workspace; 039 040/////////////////////////////////////////////////////////////////// 041//// Derivative 042 043/** 044 Output the discrete derivative of the input, y[n] = (x[n] - x[n-1])/dt, 045 where <i>dt</i> is the time gap between input events. Output is not generated 046 until two inputs have been consumed. 047 <p> 048 The output of this actor is constrained to be a double, and input must be castable 049 to a double. If the input signal is not left-continuous, the derivative will be either 050 infinite or undefined and an exception is thrown. 051 <p> 052 In postfire(), if an event is present on the <i>reset</i> port, this 053 actor resets to its initial state, and will not output until two 054 subsequent inputs have been consumed. This is useful if the input signal is 055 switched on and off, in which case the time gap between events becomes large 056 and would otherwise effect the value of the derivative for one sample. 057 <p> 058 @author Jeff C. Jensen 059 @version $Id: Derivative.java$ 060 @since Ptolemy II 8.0 061 @see ptolemy.actor.lib.Differential 062 */ 063public class Derivative extends DETransformer { 064 /** Construct an actor with the given container and name. 065 * @param container The container. 066 * @param name The name of this actor. 067 * @exception IllegalActionException If the actor cannot be contained 068 * by the proposed container. 069 * @exception NameDuplicationException If the container already has an 070 * actor with this name. 071 */ 072 public Derivative(CompositeEntity container, String name) 073 throws NameDuplicationException, IllegalActionException { 074 super(container, name); 075 reset = new TypedIOPort(this, "reset", true, false); 076 reset.setMultiport(true); 077 input.setTypeAtMost(BaseType.DOUBLE); 078 output.setTypeEquals(BaseType.DOUBLE); 079 } 080 081 /////////////////////////////////////////////////////////////////// 082 //// public methods //// 083 084 /** Clone the actor into the specified workspace. This calls the 085 * base class and then sets the ports. 086 * @param workspace The workspace for the new object. 087 * @return A new actor. 088 * @exception CloneNotSupportedException If a derived class has 089 * has an attribute that cannot be cloned. 090 */ 091 @Override 092 public Object clone(Workspace workspace) throws CloneNotSupportedException { 093 Derivative newObject = (Derivative) super.clone(workspace); 094 095 newObject.input.setTypeAtMost(BaseType.DOUBLE); 096 newObject.output.setTypeEquals(BaseType.DOUBLE); 097 098 // This is not strictly needed (since it is always recreated 099 // in preinitialize) but it is safer. 100 newObject._lastInput = null; 101 102 return newObject; 103 } 104 105 /** Consume at most one token from the <i>input</i> port and output 106 * its value minus the value of the input read in the previous 107 * iteration, divided by the time gap between the two events. 108 * If there has been no previous iteration, no output is sent. 109 * If there is no input, then produce no output. 110 * @exception IllegalActionException If subtraction or division is not 111 * supported by the supplied tokens. 112 */ 113 @Override 114 public void fire() throws IllegalActionException { 115 super.fire(); 116 if (input.hasToken(0)) { 117 Time currentTime = getDirector().getModelTime(); 118 DoubleToken currentToken = (DoubleToken) input.get(0); 119 _currentInput = new TimedEvent(currentTime, currentToken); 120 121 if (_lastInput != null) { 122 Time lastTime = _lastInput.timeStamp; 123 DoubleToken lastToken = (DoubleToken) _lastInput.contents; 124 DoubleToken timeGap = new DoubleToken( 125 currentTime.subtract(lastTime).getDoubleValue()); 126 127 //If the timeGap is zero, then we have received a simultaneous event. If the 128 // value of the input has not changed, then we can ignore this input, as a control 129 // signal was already generated. However if the value has changed, then the signal 130 // is discontinuous and an exception will be thrown. 131 if (timeGap.doubleValue() == 0 132 && !currentToken.equals(lastToken)) { 133 throw new IllegalActionException( 134 "Derivative received discontinuous input."); 135 } 136 137 output.broadcast( 138 currentToken.subtract(lastToken).divide(timeGap)); 139 } 140 } 141 } 142 143 /** Reset to indicate that no input has yet been seen. 144 * @exception IllegalActionException If the parent class throws it. 145 */ 146 @Override 147 public void initialize() throws IllegalActionException { 148 super.initialize(); 149 _lastInput = null; 150 _currentInput = null; 151 } 152 153 /** Record the most recent input as the latest input. If a reset 154 * event has been received, process it here. 155 * @exception IllegalActionException If the base class throws it. 156 */ 157 @Override 158 public boolean postfire() throws IllegalActionException { 159 //If reset port is connected and has a token, reset state. 160 if (reset.getWidth() > 0) { 161 if (reset.hasToken(0)) { 162 //Consume reset token 163 reset.get(0); 164 165 //Reset the current input 166 _currentInput = null; 167 } 168 } 169 _lastInput = _currentInput; 170 return super.postfire(); 171 } 172 173 /////////////////////////////////////////////////////////////////// 174 //// ports and parameters //// 175 176 /** The reset port, which has undeclared type. If this port 177 * receives a token, this actor resets to its initial state, 178 * and no output is generated until two inputs have been received. 179 */ 180 public TypedIOPort reset; 181 182 /////////////////////////////////////////////////////////////////// 183 //// private members //// 184 private TimedEvent _currentInput; 185 186 private TimedEvent _lastInput; 187}