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