001/* An interpolator for a specified array of indexes and values.
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.actor.lib;
029
030import ptolemy.data.ArrayToken;
031import ptolemy.data.DoubleToken;
032import ptolemy.data.IntToken;
033import ptolemy.data.expr.Parameter;
034import ptolemy.data.type.ArrayType;
035import ptolemy.data.type.BaseType;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.Attribute;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.NameDuplicationException;
040import ptolemy.kernel.util.Workspace;
041import ptolemy.math.Interpolation;
042
043///////////////////////////////////////////////////////////////////
044//// Interpolator
045
046/**
047 <p>Produce an interpolation based on the parameters.
048 This class uses the Interpolation class in the math package to compute
049 the interpolation.
050 The <i>values</i> parameter specifies a sequence of values
051 to produce at the output.  The <i>indexes</i> parameter specifies
052 when those values should be produced.
053 The values and indexes parameters must both contain
054 arrays, and have equal lengths or an exception will be thrown.
055 The <i>indexes</i> array must be increasing and non-negative.
056 The values are periodic if the <i>period</i> parameter contains a
057 positive value. In this case, the period must be greater than the
058 largest index, and values within the index range 0 to (period-1) are
059 repeated indefinitely.  If the period is zero, the values are not
060 periodic, and the values outside the range of the indexes are
061 considered to be 0.0.  The <i>order</i> parameter
062 specifies which order of interpolation to apply  whenever the
063 iteration count does not match an index in <i>indexes</i>.
064 The Interpolation class currently supports zero, first, and third
065 order interpolations. The default parameter are those set in the
066 Interpolation class.</p>
067 <p>
068 This actor counts iterations.  Whenever the iteration count matches an entry
069 in the <i>indexes</i> array, the corresponding entry (at the same position)
070 in the <i>values</i> array is produced at the output.  Whenever the iteration
071 count does not match a value in the <i>indexes</i> array, an interpolation
072 of the values is produced at the output.</p>
073 <p>
074 Output type is DoubleToken.</p>
075
076 @author Sarah Packman, Yuhong Xiong
077 @version $Id$
078 @since Ptolemy II 0.3
079 @Pt.ProposedRating Yellow (yuhong)
080 @Pt.AcceptedRating Yellow (yuhong)
081 @see ptolemy.math.Interpolation
082 */
083public class Interpolator extends SequenceSource {
084    /** Construct an actor with the specified container and name.
085     *  @param container The container.
086     *  @param name The name of this actor.
087     *  @exception IllegalActionException If the entity cannot be contained
088     *   by the proposed container.
089     *  @exception NameDuplicationException If the container already has an
090     *   actor with this name.
091     */
092    public Interpolator(CompositeEntity container, String name)
093            throws NameDuplicationException, IllegalActionException {
094        super(container, name);
095
096        _interpolation = new Interpolation();
097
098        // Initialize the parameters with the default settings of the
099        // Interpolation class. This is not required for this class to
100        // function. But since these parameters are public, other objects
101        // in the system may use them.
102        indexes = new Parameter(this, "indexes");
103        indexes.setExpression("{0, 1}");
104        indexes.setTypeEquals(new ArrayType(BaseType.INT));
105
106        // Call this so that we don't have to copy its code here...
107        attributeChanged(indexes);
108
109        // set values parameter
110        values = new Parameter(this, "values");
111        values.setExpression("{1.0, 0.0}");
112        values.setTypeEquals(new ArrayType(BaseType.DOUBLE));
113
114        int defOrder = _interpolation.getOrder();
115        IntToken defOrderToken = new IntToken(defOrder);
116        order = new Parameter(this, "order", defOrderToken);
117        order.setTypeEquals(BaseType.INT);
118
119        int defPeriod = _interpolation.getPeriod();
120        IntToken defPeriodToken = new IntToken(defPeriod);
121        period = new Parameter(this, "period", defPeriodToken);
122        period.setTypeEquals(BaseType.INT);
123
124        output.setTypeEquals(BaseType.DOUBLE);
125
126        // Show the firingCountLimit parameter last.
127        firingCountLimit.moveToLast();
128    }
129
130    ///////////////////////////////////////////////////////////////////
131    ////                         public variables                  ////
132
133    /** The indexes at which the specified values will be produced.
134     *  This parameter is an array of integers, with default value {0, 1}.
135     */
136    public Parameter indexes;
137
138    /** The order of interpolation for non-index iterations.
139     *  This parameter must contain an IntToken.
140     */
141    public Parameter order;
142
143    /** The period of the reference values.
144     *  This parameter must contain an IntToken.
145     */
146    public Parameter period;
147
148    /** The values that will be produced at the specified indexes.
149     *  This parameter is an array, with default value {1.0, 0.0}.
150     */
151    public Parameter values;
152
153    ///////////////////////////////////////////////////////////////////
154    ////                         public methods                    ////
155
156    /** Check the validity of the parameter.
157     *  @param attribute The attribute that changed.
158     *  @exception IllegalActionException If the argument is the
159     *   <i>values</i> parameter and it does not contain an one dimensional
160     *   array; or the argument is the <i>indexes</i> parameter and it does
161     *   not contain an one dimensional array or is not increasing and
162     *   non-negative; or the argument is the <i>period</i> parameter and is
163     *   negative; or the argument is the <i>order</i> parameter and the order
164     *   is not supported by the Interpolation class.
165     */
166    @Override
167    public void attributeChanged(Attribute attribute)
168            throws IllegalActionException {
169        if (attribute == values) {
170            ArrayToken valuesValue = (ArrayToken) values.getToken();
171            _values = new double[valuesValue.length()];
172
173            for (int i = 0; i < valuesValue.length(); i++) {
174                _values[i] = ((DoubleToken) valuesValue.getElement(i))
175                        .doubleValue();
176            }
177
178            _interpolation.setValues(_values);
179        } else if (attribute == indexes) {
180            ArrayToken indexesValue = (ArrayToken) indexes.getToken();
181            _indexes = new int[indexesValue.length()];
182
183            int previous = 0;
184
185            for (int i = 0; i < indexesValue.length(); i++) {
186                _indexes[i] = ((IntToken) indexesValue.getElement(i))
187                        .intValue();
188
189                // Check nondecreasing property.
190                if (_indexes[i] < previous) {
191                    throw new IllegalActionException(this,
192                            "Value of indexes is not nondecreasing "
193                                    + "and nonnegative.");
194                }
195
196                previous = _indexes[i];
197            }
198
199            _interpolation.setIndexes(_indexes);
200        } else if (attribute == period) {
201            int newPeriod = ((IntToken) period.getToken()).intValue();
202            _interpolation.setPeriod(newPeriod);
203        } else if (attribute == order) {
204            int newOrder = ((IntToken) order.getToken()).intValue();
205            _interpolation.setOrder(newOrder);
206        } else {
207            super.attributeChanged(attribute);
208        }
209    }
210
211    /** Clone the actor into the specified workspace. This calls the
212     *  base class and then initializes private variables.
213     *  public members to the parameters of the new actor.
214     *  @param workspace The workspace for the new object.
215     *  @return A new actor.
216     *  @exception CloneNotSupportedException If a derived class contains
217     *   an attribute that cannot be cloned.
218     */
219    @Override
220    public Object clone(Workspace workspace) throws CloneNotSupportedException {
221        Interpolator newObject = (Interpolator) super.clone(workspace);
222
223        newObject._indexes = new int[_indexes.length];
224        System.arraycopy(_indexes, 0, newObject._indexes, 0, _indexes.length);
225
226        newObject._interpolation = new Interpolation();
227
228        if (_values == null) {
229            newObject._values = null;
230        } else {
231            newObject._values = new double[_values.length];
232            System.arraycopy(_values, 0, newObject._values, 0, _values.length);
233        }
234        return newObject;
235    }
236
237    /** Output the value at the current iteration count. The output is
238     *  one of the reference values if the iteration count matches one
239     *  of the indexes, or is interpolated otherwise.
240     *  @exception IllegalActionException If the <i>values</i> and
241     *   <i>indexes</i> parameters do not contain arrays of the same length,
242     *   or the period is not 0 and not greater than the largest index.
243     */
244    @Override
245    public void fire() throws IllegalActionException {
246        super.fire();
247
248        double result = _interpolation.interpolate(_iterationCount);
249        output.send(0, new DoubleToken(result));
250    }
251
252    /** Set the iteration count to zero.
253     *  @exception IllegalActionException If the super class throws it.
254     */
255    @Override
256    public void initialize() throws IllegalActionException {
257        super.initialize();
258        _iterationCount = 0;
259    }
260
261    /** Update the iteration counter, then call the super class method.
262     *  @return A boolean returned by the super class method.
263     *  @exception IllegalActionException If the super class throws it.
264     */
265    @Override
266    public boolean postfire() throws IllegalActionException {
267        ++_iterationCount;
268        return super.postfire();
269    }
270
271    ///////////////////////////////////////////////////////////////////
272    ////                         private variables                 ////
273    // Cache of indexes array value.
274    private transient int[] _indexes;
275
276    private int _iterationCount = 0;
277
278    private Interpolation _interpolation;
279
280    // Cache of values array value.
281    private transient double[] _values;
282}