001/* An FIR lattice filter.
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.expr.Parameter;
033import ptolemy.data.type.BaseType;
034import ptolemy.kernel.CompositeEntity;
035import ptolemy.kernel.util.Attribute;
036import ptolemy.kernel.util.IllegalActionException;
037import ptolemy.kernel.util.NameDuplicationException;
038import ptolemy.kernel.util.Workspace;
039
040///////////////////////////////////////////////////////////////////
041//// Lattice
042
043/**
044 An FIR filter with a lattice structure.  The coefficients of such a
045 filter are called "reflection coefficients."  Lattice filters are
046 typically used as linear predictors because it is easy to ensure that
047 they are minimum phase, and hence that their inverse is stable.
048 A lattice filter is (strictly) minimum phase if its reflection
049 coefficients are all less than unity in magnitude.  To get the
050 reflection coefficients for a linear predictor for a particular
051 random process, you can use the LevinsonDurbin actor.
052 The inputs and outputs are of type double.
053 <p>
054 The default reflection coefficients correspond to the optimal linear
055 predictor for an AR process generated by filtering white noise with
056 the following filter:
057 <pre>
058                            1
059 H(z) =  --------------------------------------
060         1 - 2z<sup>-1</sup> + 1.91z<sup>-2</sup> - 0.91z<sup>-3</sup> + 0.205z<sup>-4</sup>
061 </pre>
062 <p>
063 Since this filter is minimum phase, the transfer function of the lattice
064 filter is <i>H </i><sup>-1</sup>(<i>z</i>).
065 <p>
066 Note that the definition of reflection coefficients is not quite universal
067 in the literature. The reflection coefficients in reference [2]
068 are the negative of the ones used by this actor, which
069 correspond to the definition in most other texts,
070 and to the definition of partial-correlation (PARCOR)
071 coefficients in the statistics literature.
072 <p>
073 The signs of the coefficients used in this actor are appropriate for values
074 given by the LevinsonDurbin actor.
075 The structure of the filter is as follows:
076 <pre>
077      y[0]              y[1]               y[n-1]           y[n]
078 X(n) -------o--&gt;-(+)--&gt;----o--&gt;-(+)--&gt;-- ... -&gt;---o--&gt;-(+)------&gt;  Y(n)
079      |       \   /          \   /                  \   /
080      |      +K1 /          +K2 /                  +Kn /
081      |         X              X                      X
082      V      -K1 \          -K2 \                  -Kn \
083      |       /   \          /   \                  /   \
084      \-[z]--o--&gt;-(+)-[z]---o--&gt;-(+)-[z]- ... -&gt;---o--&gt;-(+)
085           w[0]         w[1]               w[n-1]           w[n]
086 </pre>
087 <p>
088 <b>References</b>
089 <p>[1]
090 J. Makhoul, "Linear Prediction: A Tutorial Review",
091 <i>Proc. IEEE</i>, Vol. 63, pp. 561-580, Apr. 1975.
092 <p>[2]
093 S. M. Kay, <i>Modern Spectral Estimation: Theory &amp; Application</i>,
094 Prentice-Hall, Englewood Cliffs, NJ, 1988.
095
096 @see ptolemy.domains.sdf.lib.FIR
097 @see LevinsonDurbin
098 @see RecursiveLattice
099 @see ptolemy.domains.sdf.lib.VariableLattice
100 @author Edward A. Lee, Christopher Hylands, Steve Neuendorffer
101 @version $Id$
102 @since Ptolemy II 1.0
103 @Pt.ProposedRating Yellow (eal)
104 @Pt.AcceptedRating Yellow (cxh)
105 */
106public class Lattice extends Transformer {
107    /** Construct an actor with the given container and name.
108     *  @param container The container.
109     *  @param name The name of this actor.
110     *  @exception IllegalActionException If the actor cannot be contained
111     *   by the proposed container.
112     *  @exception NameDuplicationException If the container already has an
113     *   actor with this name.
114     */
115    public Lattice(CompositeEntity container, String name)
116            throws NameDuplicationException, IllegalActionException {
117        super(container, name);
118
119        input.setTypeEquals(BaseType.DOUBLE);
120        output.setTypeEquals(BaseType.DOUBLE);
121
122        reflectionCoefficients = new Parameter(this, "reflectionCoefficients");
123
124        // Note that setExpression() will call attributeChanged().
125        reflectionCoefficients
126                .setExpression("{0.804534, -0.820577, 0.521934, -0.205}");
127    }
128
129    ///////////////////////////////////////////////////////////////////
130    ////                         parameters                        ////
131
132    /** The reflection coefficients.  This is an array of doubles with
133     *  default value {0.804534, -0.820577, 0.521934, -0.205}. These
134     *  are the reflection coefficients for the linear predictor of a
135     *  particular random process.
136     */
137    public Parameter reflectionCoefficients;
138
139    ///////////////////////////////////////////////////////////////////
140    ////                         public methods                    ////
141
142    /** If the argument is the <i>reflectionCoefficients</i> parameter,
143     *  then reallocate the arrays to use.
144     *  @param attribute The attribute that changed.
145     *  @exception IllegalActionException If the base class throws it.
146     */
147    @Override
148    public void attributeChanged(Attribute attribute)
149            throws IllegalActionException {
150        if (attribute == reflectionCoefficients) {
151            ArrayToken value = (ArrayToken) reflectionCoefficients.getToken();
152            _order = value.length();
153
154            if (_backward == null || _order != _backward.length) {
155                // Need to reallocate the arrays.
156                _reallocate();
157            }
158
159            for (int i = 0; i < _order; i++) {
160                _reflectionCoefficients[i] = ((DoubleToken) value.getElement(i))
161                        .doubleValue();
162            }
163        } else {
164            super.attributeChanged(attribute);
165        }
166    }
167
168    /** Clone the actor into the specified workspace.
169     *  @param workspace The workspace for the new object.
170     *  @return A new actor.
171     *  @exception CloneNotSupportedException If a derived class contains
172     *   an attribute that cannot be cloned.
173     */
174    @Override
175    public Object clone(Workspace workspace) throws CloneNotSupportedException {
176        Lattice newObject = (Lattice) super.clone(workspace);
177
178        newObject._backward = new double[newObject._order + 1];
179        newObject._backwardCache = new double[newObject._order + 1];
180        newObject._forward = new double[newObject._order + 1];
181        newObject._forwardCache = new double[newObject._order + 1];
182        newObject._reflectionCoefficients = new double[newObject._order];
183
184        if (_backward != null) {
185            System.arraycopy(_backward, 0, newObject._backward, 0,
186                    _backward.length);
187        }
188        if (_backwardCache != null) {
189            System.arraycopy(_backwardCache, 0, newObject._backwardCache, 0,
190                    _backwardCache.length);
191        }
192        if (_forward != null) {
193            System.arraycopy(_forward, 0, newObject._forward, 0,
194                    _forward.length);
195        }
196        if (_forwardCache != null) {
197            System.arraycopy(_forwardCache, 0, newObject._forwardCache, 0,
198                    _forwardCache.length);
199        }
200        if (_reflectionCoefficients != null) {
201            System.arraycopy(_reflectionCoefficients, 0,
202                    newObject._reflectionCoefficients, 0,
203                    _reflectionCoefficients.length);
204        }
205
206        try {
207            ArrayToken value = (ArrayToken) reflectionCoefficients.getToken();
208            for (int i = 0; i < _order; i++) {
209                _reflectionCoefficients[i] = ((DoubleToken) value.getElement(i))
210                        .doubleValue();
211            }
212        } catch (IllegalActionException ex) {
213            // CloneNotSupportedException does not have a constructor
214            // that takes a cause argument, so we use initCause
215            CloneNotSupportedException throwable = new CloneNotSupportedException();
216            throwable.initCause(ex);
217            throw throwable;
218        }
219        return newObject;
220    }
221
222    /** Consume one input token, if there is one, and produce one output
223     *  token.  If there is no input, the produce no output.
224     *  @exception IllegalActionException If there is no director.
225     */
226    @Override
227    public void fire() throws IllegalActionException {
228        super.fire();
229        if (input.hasToken(0)) {
230            DoubleToken in = (DoubleToken) input.get(0);
231
232            _forwardCache[0] = in.doubleValue(); // _forwardCache(0) = x(n)
233
234            _doFilter();
235
236            _backwardCache[0] = _forwardCache[0]; // _backwardCache[0] = x[n]
237
238            // Send the forward residual.
239            output.broadcast(new DoubleToken(_forwardCache[_order]));
240        }
241    }
242
243    /** Initialize the state of the filter.
244     */
245    @Override
246    public void initialize() throws IllegalActionException {
247        super.initialize();
248        for (int i = 0; i < _order + 1; i++) {
249            _forward[i] = 0.0;
250            _forwardCache[i] = 0.0;
251            _backward[i] = 0.0;
252            _backwardCache[i] = 0.0;
253        }
254    }
255
256    /** Update the backward and forward prediction errors that
257     *  were generated in fire() method.
258     *  @return False if the number of iterations matches the number requested.
259     *  @exception IllegalActionException If there is no director.
260     */
261    @Override
262    public boolean postfire() throws IllegalActionException {
263        System.arraycopy(_backwardCache, 0, _backward, 0, _order + 1);
264        System.arraycopy(_forwardCache, 0, _forward, 0, _order + 1);
265        return super.postfire();
266    }
267
268    /** Check to see if this actor is ready to fire.
269     *  @exception IllegalActionException If there is no director.
270     */
271    @Override
272    public boolean prefire() throws IllegalActionException {
273        // Get a copy of the current filter state that we can modify.
274        System.arraycopy(_backward, 0, _backwardCache, 0, _order + 1);
275        System.arraycopy(_forward, 0, _forwardCache, 0, _order + 1);
276        return super.prefire();
277    }
278
279    ///////////////////////////////////////////////////////////////////
280    ////                         protected methods                 ////
281
282    /** Compute the filter, updating the caches, based on the current
283     *  values.
284     *  @exception IllegalActionException Not thrown in this base class.
285     */
286    protected void _doFilter() throws IllegalActionException {
287        double k;
288
289        // NOTE: The following code is ported from Ptolemy Classic.
290        // Update forward errors.
291        for (int i = 0; i < _order; i++) {
292            k = _reflectionCoefficients[i];
293            _forwardCache[i + 1] = -k * _backwardCache[i] + _forwardCache[i];
294        }
295
296        // Backward: Compute the weights for the next round Note:
297        // strictly speaking, _backwardCache[_order] is not necessary
298        // for computing the output.  It is computed for the use of
299        // subclasses which adapt the reflection coefficients.
300        for (int i = _order; i > 0; i--) {
301            k = _reflectionCoefficients[i - 1];
302            _backwardCache[i] = -k * _forwardCache[i - 1]
303                    + _backwardCache[i - 1];
304        }
305    }
306
307    /**  Reallocate the internal arrays. */
308    protected void _reallocate() {
309        _backward = new double[_order + 1];
310        _backwardCache = new double[_order + 1];
311        _forward = new double[_order + 1];
312        _forwardCache = new double[_order + 1];
313        _reflectionCoefficients = new double[_order];
314    }
315
316    ///////////////////////////////////////////////////////////////////
317    ////                         private variables                 ////
318
319    /** The order of the filter (i.e. the number of reflection coefficients) */
320    protected int _order = 0;
321
322    /** Backward prediction errors.  The length is _order. */
323    protected double[] _backward = null;
324
325    /** Cache of backward prediction errors.
326     * The fire() method updates _forwardCache and postfire()
327     * copies _forwardCache to _forward so this actor will work in domains
328     * like SR.  The length is _order.
329     */
330    protected double[] _backwardCache = null;
331
332    /** Forward prediction errors.  The length is _order + 1. */
333    protected double[] _forward = null;
334
335    /** Cache of forward prediction errors.
336     * The fire() method updates _forwardCache and postfire()
337     * copies _forwardCache to _forward so this actor will work in domains
338     * like SR.  The length is _order + 1.
339     */
340    protected double[] _forwardCache = null;
341
342    /** Cache of reflection coefficients.   The length is _order. */
343    protected double[] _reflectionCoefficients = null;
344}