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-->-(+)-->----o-->-(+)-->-- ... ->---o-->-(+)------> Y(n) 079 | \ / \ / \ / 080 | +K1 / +K2 / +Kn / 081 | X X X 082 V -K1 \ -K2 \ -Kn \ 083 | / \ / \ / \ 084 \-[z]--o-->-(+)-[z]---o-->-(+)-[z]- ... ->---o-->-(+) 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 & 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}