001/* A Quanitzed Sampler 002 Below is the copyright agreement for the Ptolemy II system. 003 004 Copyright (c) 2014-2018 The Regents of the University of California. 005 All rights reserved. 006 007 Permission is hereby granted, without written agreement and without 008 license or royalty fees, to use, copy, modify, and distribute this 009 software and its documentation for any purpose, provided that the above 010 copyright notice and the following two paragraphs appear in all copies 011 of this software. 012 013 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 014 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 015 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 016 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 017 SUCH DAMAGE. 018 019 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 020 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 021 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 022 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 023 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 024 ENHANCEMENTS, OR MODIFICATIONS. 025 026 PT_COPYRIGHT_VERSION_2 027 COPYRIGHTENDKEY 028 029*/ 030package ptolemy.domains.de.lib; 031 032import ptolemy.actor.lib.Transformer; 033import ptolemy.data.DoubleToken; 034import ptolemy.data.SmoothToken; 035import ptolemy.data.expr.Parameter; 036import ptolemy.data.type.BaseType; 037import ptolemy.kernel.CompositeEntity; 038import ptolemy.kernel.util.Attribute; 039import ptolemy.kernel.util.IllegalActionException; 040import ptolemy.kernel.util.NameDuplicationException; 041 042/////////////////////////////////////////////////////////////////// 043//// QuantizedSampler 044 045/** 046 * Output the input if the signal has changed more than a quantum 047 * value since the last input. 048 * 049 * <p>This quantizer is designed to take an input signal and send it to its output port 050 * if the signal has changed by more than a quantum relative to the last signal seen 051 * at the input port.</p> 052 * 053 * <p>The number of outputs produced by this actor is dependent on the 054 * input values, so it should not be used with domains like SDF.</p> 055 * 056 * @author Thierry S. Nouidui, contributor: Christopher Brooks 057 * @version $Id$ 058 * @since Ptolemy II 11.0 059 * @Pt.ProposedRating Yellow (eal) 060 * @Pt.AcceptedRating Red (cxh) 061 */ 062public class QuantizedSampler extends Transformer { 063 064 /** Construct a new instance of the quantizer. 065 * @param container The container. 066 * @param name The name. 067 * @exception IllegalActionException If setting up ports and 068 * parameters fails. 069 * @exception NameDuplicationException If the container already 070 * contains an object with this name. 071 */ 072 public QuantizedSampler(CompositeEntity container, String name) 073 throws IllegalActionException, NameDuplicationException { 074 super(container, name); 075 input.setTypeEquals(BaseType.DOUBLE); 076 output.setTypeEquals(BaseType.DOUBLE); 077 078 quantum = new Parameter(this, "quantum"); 079 quantum.setTypeEquals(BaseType.DOUBLE); 080 quantum.setExpression("1.0"); 081 082 attributeChanged(quantum); 083 } 084 085 /////////////////////////////////////////////////////////////////// 086 //// ports and parameters //// 087 088 /** The quantum. The default value is a double with 089 * the value 1.0, which means that if the input changes by more 090 * than 1.0, the new output will be produced. 091 */ 092 public Parameter quantum; 093 094 /////////////////////////////////////////////////////////////////// 095 //// public methods //// 096 097 /** If the argument is the quantum parameter, then cache 098 * the value. 099 * @param attribute The attribute that changed. 100 * @exception IllegalActionException If there is a problem 101 * getting the value of the quantum parameter. 102 */ 103 @Override 104 public void attributeChanged(Attribute attribute) 105 throws IllegalActionException { 106 if (attribute == quantum) { 107 _quantum = ((DoubleToken) quantum.getToken()).doubleValue(); 108 } else { 109 super.attributeChanged(attribute); 110 } 111 } 112 113 /** 114 * Produce an output equal to the input if the input has crossed 115 * the quantum; otherwise, produce no output. 116 * 117 * @exception IllegalActionException If sending an output fails. 118 */ 119 @Override 120 public void fire() throws IllegalActionException { 121 super.fire(); 122 if (input.hasToken(0)) { 123 DoubleToken newInputToken = DoubleToken.convert(input.get(0)); 124 if (_firstFiring) { 125 // Initialize last input token with first input token received. 126 _lastInputToken = newInputToken; 127 // Send first input token received to the output port. 128 output.send(0, newInputToken); 129 _firstFiring = false; 130 return; 131 } 132 final double newInput = newInputToken.doubleValue(); 133 final double lastInput = _lastInputToken.doubleValue(); 134 if (newInputToken instanceof SmoothToken) { 135 if (!_compareSmoothTokenDerivatives(newInputToken, 136 _lastInputToken)) { 137 // If the derivatives are different, send new token to the 138 // output port without doing any further comparison. 139 _lastInputToken = newInputToken; 140 output.send(0, newInputToken); 141 } else { 142 // If the derivatives are the same, check if the input 143 // has crossed the quantum. 144 if (Math.abs(newInput - lastInput) > Math.abs((_quantum))) { 145 _lastInputToken = newInputToken; 146 output.send(0, newInputToken); 147 } 148 } 149 } else { 150 if (Math.abs(newInput - lastInput) > Math.abs((_quantum))) { 151 _lastInputToken = newInputToken; 152 output.send(0, new DoubleToken(newInput)); 153 } 154 } 155 } 156 } 157 158 /** 159 * Initialize this actor. 160 */ 161 @Override 162 public void initialize() throws IllegalActionException { 163 super.initialize(); 164 _lastInputToken = null; 165 _firstFiring = true; 166 } 167 168 /////////////////////////////////////////////////////////////////// 169 //// private methods //// 170 /** 171 * Compare the derivative values of two smooth token. 172 * @param newToken The new input token received at the port. 173 * @param newToken The last input token seen at the port. 174 * @retun True if the derivatives are identical. 175 */ 176 private boolean _compareSmoothTokenDerivatives(DoubleToken newToken, 177 DoubleToken lastToken) { 178 // Now we just have to check the derivatives. 179 double[] derivativesNewToken = ((SmoothToken) newToken) 180 .derivativeValues(); 181 double[] derivativesLastToken = ((SmoothToken) lastToken) 182 .derivativeValues(); 183 if (derivativesNewToken == derivativesLastToken) { 184 // Derivatives are identical (should be true only if null). 185 return true; 186 } 187 if (derivativesNewToken == null && derivativesLastToken != null 188 // Findbugs wants us to check for null here to avoid dereferencing 189 // a null when we check the length below. 190 || derivativesNewToken == null && derivativesLastToken == null 191 || derivativesNewToken != null 192 && derivativesLastToken == null) { 193 return false; 194 } 195 // Both tokens have derivatives. 196 if (derivativesNewToken.length != derivativesLastToken.length) { 197 return false; 198 } 199 // Both tokens have the same number of derivatives. 200 for (int i = 0; i < derivativesLastToken.length; i++) { 201 if (derivativesNewToken[i] != derivativesLastToken[i]) { 202 return false; 203 } 204 } 205 return true; 206 } 207 208 /////////////////////////////////////////////////////////////////// 209 //// private variables //// 210 211 /** The last recent input. */ 212 private DoubleToken _lastInputToken; 213 214 /** Quantum. */ 215 private double _quantum; 216 217 /** Flag to indicate first firing. */ 218 private boolean _firstFiring; 219}