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}