001/* Calculate the coefficients of a linear predictor. 002 003 Copyright (c) 1997-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.actor.TypedAtomicActor; 031import ptolemy.actor.TypedIOPort; 032import ptolemy.data.ArrayToken; 033import ptolemy.data.DoubleToken; 034import ptolemy.data.type.ArrayType; 035import ptolemy.data.type.BaseType; 036import ptolemy.kernel.CompositeEntity; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.NameDuplicationException; 039import ptolemy.math.SignalProcessing; 040 041/////////////////////////////////////////////////////////////////// 042//// LevinsonDurbin 043 044/** 045 This actor uses the Levinson-Durbin algorithm to compute the linear 046 predictor coefficients of a random process, given its autocorrelation 047 function as an input. 048 049 <p>These coefficients are produced both in 050 tapped delay line form (on the <i>linearPredictor</i> output) and in 051 lattice filter form (on the <i>reflectionCoefficients</i> output). 052 The <i>order</i> of the predictor (the number of <i>linearPredictor</i> 053 and coefficients <i>reflectionCoefficients</i> produced) is the 054 number of lags of the supplied autocorrelation. 055 The <i>errorPower</i> output is the power of the prediction error 056 as a function of the predictor order. 057 The inputs and outputs are all arrays of doubles.</p> 058 059 <p>The autocorrelation estimates provided as inputs can be generated 060 by the Autocorrelation actor. It the Autocorrelation actor is set 061 so that its <i>biased</i> parameter is true, then the combined 062 effect of that actor and this one is called the autocorrelation 063 method. The <i>order</i> of the predictor is the value of the 064 <i>numberOfLags</i> parameter of the Autocorrelation actor. 065 If the length of the autocorrelation input is odd, then it is assumed 066 to be a symmetric autocorrelation function, and the <i>order</i> of the 067 predictor calculated by this actor is (length + 1)/2. Otherwise, 068 the <i>order</i> is 1 + (length/2), which assumes that discarding the last 069 sample of the autocorrelation would make it symmetric.</p> 070 <p> 071 Three output signals are generated by this actor. On the 072 <i>errorPower</i> output port, an array of length <i>order</i> + 1 073 gives the prediction error power for each predictor order from zero 074 to <i>order</i>. The first value in this array, which corresponds 075 to the zeroth-order predictor, is simply the zero-th lag of the 076 input autocorrelation, which is the power of the random process 077 with that autocorrelation. Note that for signals without noise 078 whose autocorrelations are estimated by the Autocorrelation actor, 079 the <i>errorPower</i> output can get small. If it gets close 080 to zero, or goes negative, this actor fixes it at zero. 081 "Close to" is determined by the close() method of the 082 ptolemy.math.SignalProcessing class.</p> 083 <p> 084 The <i>linearPredictor</i> output gives the coefficients of an 085 FIR filter that performs linear prediction for the random process. 086 This set of coefficients is suitable for directly feeding a 087 VariableFIR actor, which accepts outside coefficients. 088 The number of coefficients produced is equal to the <i>order</i>. 089 The predictor coefficients produced by this actor can be 090 used to create a maximum-entropy spectral estimate of the input 091 to the Autocorrelation actor. They can also be used for 092 linear-predictive coding, and any number of other applications.</p> 093 <p> 094 The <i>reflectionCoefficients</i> output is the reflection 095 coefficients, suitable for feeding directly to a VariableLattice 096 actor, which will then generate the forward and backward prediction error. 097 The number of coefficients produced is equal to the <i>order</i>.</p> 098 <p> 099 Note that the definition of reflection coefficients is not quite 100 universal in the literature. The reflection coefficients in 101 reference [2] is the negative of the ones generated by this actor, 102 which correspond to the definition in most other texts, 103 and to the definition of partial-correlation (PARCOR) 104 coefficients in the statistics literature.</p> 105 <p><b>References</b></p> 106 <p>[1] 107 J. Makhoul, "Linear Prediction: A Tutorial Review", 108 <i>Proc. IEEE</i>, vol. 63, pp. 561-580, Apr. 1975.</p> 109 <p>[2] 110 S. M. Kay, <i>Modern Spectral Estimation: Theory & Application</i>, 111 Prentice-Hall, Englewood Cliffs, NJ, 1988.</p> 112 113 @see ptolemy.domains.sdf.lib.Autocorrelation 114 @see ptolemy.domains.sdf.lib.VariableFIR 115 @see ptolemy.domains.sdf.lib.VariableLattice 116 @see ptolemy.math.SignalProcessing 117 118 @author Edward A. Lee 119 @version $Id$ 120 @since Ptolemy II 1.0 121 @Pt.ProposedRating Yellow (eal) 122 @Pt.AcceptedRating Red (cxh) 123 */ 124public class LevinsonDurbin extends TypedAtomicActor { 125 /** Construct an actor in the specified container with the specified 126 * name. 127 * @param container The container. 128 * @param name The name. 129 * @exception IllegalActionException If the actor cannot be contained 130 * by the proposed container. 131 * @exception NameDuplicationException If the name coincides with 132 * an actor already in the container. 133 */ 134 public LevinsonDurbin(CompositeEntity container, String name) 135 throws IllegalActionException, NameDuplicationException { 136 super(container, name); 137 138 autocorrelation = new TypedIOPort(this, "autocorrelation", true, false); 139 errorPower = new TypedIOPort(this, "errorPower", false, true); 140 linearPredictor = new TypedIOPort(this, "linearPredictor", false, true); 141 reflectionCoefficients = new TypedIOPort(this, "reflectionCoefficients", 142 false, true); 143 144 // FIXME: Can the inputs be complex? 145 autocorrelation.setTypeEquals(new ArrayType(BaseType.DOUBLE)); 146 errorPower.setTypeEquals(new ArrayType(BaseType.DOUBLE)); 147 linearPredictor.setTypeEquals(new ArrayType(BaseType.DOUBLE)); 148 reflectionCoefficients.setTypeEquals(new ArrayType(BaseType.DOUBLE)); 149 } 150 151 /////////////////////////////////////////////////////////////////// 152 //// ports and parameters //// 153 154 /** The autocorrelation input, which is an array. 155 */ 156 public TypedIOPort autocorrelation; 157 158 /** The output for the error power, as a function of the predictor 159 * order. This produces an array. 160 */ 161 public TypedIOPort errorPower; 162 163 /** The output for linear predictor coefficients. 164 * This produces an array. 165 */ 166 public TypedIOPort linearPredictor; 167 168 /** The output for lattice filter coefficients for a prediction 169 * error filter. This produces an array. 170 */ 171 public TypedIOPort reflectionCoefficients; 172 173 /////////////////////////////////////////////////////////////////// 174 //// public methods //// 175 176 /** Consume the autocorrelation input, and calculate the predictor 177 * coefficients, reflection coefficients, and prediction error power. 178 * @exception IllegalActionException If there is no director. 179 */ 180 @Override 181 public void fire() throws IllegalActionException { 182 super.fire(); 183 ArrayToken autocorrelationValue = (ArrayToken) autocorrelation.get(0); 184 int autocorrelationValueLength = autocorrelationValue.length(); 185 186 // If the length of the input is odd, then the order is 187 // (length + 1)/2. Otherwise, it is 1 + length/2. 188 // Both numbers are the result of integer division 189 // (length + 2)/2. 190 int order = autocorrelationValueLength / 2; 191 192 DoubleToken[] power = new DoubleToken[order + 1]; 193 DoubleToken[] refl = new DoubleToken[order]; 194 DoubleToken[] lp = new DoubleToken[order]; 195 double[] a = new double[order + 1]; 196 double[] aP = new double[order + 1]; 197 double[] r = new double[order + 1]; 198 199 a[0] = 1.0; 200 aP[0] = 1.0; 201 202 // For convenience, read the autocorrelation lags into a vector. 203 for (int i = 0; i <= order; i++) { 204 r[i] = ((DoubleToken) autocorrelationValue 205 .getElement(autocorrelationValueLength - order + i - 1)) 206 .doubleValue(); 207 } 208 209 // Output the zeroth order prediction error power, which is 210 // simply the power of the input process. 211 double P = r[0]; 212 power[0] = new DoubleToken(P); 213 214 double gamma; 215 216 // The order recurrence 217 for (int M = 0; M < order; M++) { 218 // Compute the new reflection coefficient. 219 double deltaM = 0.0; 220 221 for (int m = 0; m < M + 1; m++) { 222 deltaM += a[m] * r[M + 1 - m]; 223 } 224 225 // Compute and output the reflection coefficient 226 // (which is also equal to the last AR parameter). 227 if (SignalProcessing.close(P, 0.0)) { 228 aP[M + 1] = gamma = 0.0; 229 } else { 230 aP[M + 1] = gamma = -deltaM / P; 231 } 232 233 refl[M] = new DoubleToken(-gamma); 234 235 for (int m = 1; m < M + 1; m++) { 236 aP[m] = a[m] + gamma * a[M + 1 - m]; 237 } 238 239 // Update the prediction error power. 240 P = P * (1.0 - gamma * gamma); 241 242 if (P < 0.0 || SignalProcessing.close(P, 0.0)) { 243 P = 0.0; 244 } 245 246 power[M + 1] = new DoubleToken(P); 247 248 // Swap a and aP for next order recurrence. 249 double[] temp = a; 250 a = aP; 251 aP = temp; 252 } 253 254 // Generate the lp outputs. 255 for (int m = 1; m <= order; m++) { 256 lp[m - 1] = new DoubleToken(-a[m]); 257 } 258 259 linearPredictor.broadcast(new ArrayToken(BaseType.DOUBLE, lp)); 260 reflectionCoefficients.broadcast(new ArrayToken(BaseType.DOUBLE, refl)); 261 errorPower.broadcast(new ArrayToken(BaseType.DOUBLE, power)); 262 } 263 264 /** If there is no token on the <i>autocorrelation</i> input, return 265 * false. Otherwise, return whatever the base class returns. 266 * @exception IllegalActionException If the base class throws it. 267 * @return True if it is ok to continue. 268 */ 269 @Override 270 public boolean prefire() throws IllegalActionException { 271 if (!autocorrelation.hasToken(0)) { 272 return false; 273 } 274 275 return super.prefire(); 276 } 277}