001/* A library of signal processing operations. 002 003 The algorithms for the FFT and DCT are based on the FFCT algorithm 004 described in: 005 006 Martin Vetterli and Henri J. Nussbaumer."Simple FFT and DCT Algorithms with 007 Reduced Number of Operations". Signal Processing 6 (1984) 267-278. 008 009 Copyright (c) 1998-2018 The Regents of the University of California. 010 All rights reserved. 011 012 Permission is hereby granted, without written agreement and without 013 license or royalty fees, to use, copy, modify, and distribute this 014 software and its documentation for any purpose, provided that the above 015 copyright notice and the following two paragraphs appear in all copies 016 of this software. 017 018 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 019 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 020 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 021 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 022 SUCH DAMAGE. 023 024 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 025 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 026 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 027 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 028 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 029 ENHANCEMENTS, OR MODIFICATIONS. 030 031 PT_COPYRIGHT_VERSION_2 032 COPYRIGHTENDKEY 033 034 035 */ 036package ptolemy.math; 037 038/////////////////////////////////////////////////////////////////// 039//// SignalProcessing 040 041/** 042 This class provides signal processing functions. 043 044 The algorithms for the FFT and DCT are based on the FFCT algorithm 045 described in: 046 047 Martin Vetterli and Henri J. Nussbaumer."Simple FFT and DCT Algorithms with 048 Reduced Number of Operations". Signal Processing 6 (1984) 267-278. 049 050 @author Albert Chen, William Wu, Edward A. Lee, Jeff Tsay, Elaine Cheong 051 @version $Id$ 052 @since Ptolemy II 0.2 053 @Pt.ProposedRating Yellow (ctsay) 054 @Pt.AcceptedRating Red (cxh) 055 */ 056public class SignalProcessing { 057 // The only constructor is private so that this class cannot 058 // be instantiated. 059 private SignalProcessing() { 060 } 061 062 /////////////////////////////////////////////////////////////////// 063 //// public methods //// 064 065 /** Return true if the first argument is close to the second (within 066 * EPSILON, where EPSILON is a static public variable of this class). 067 */ 068 public static final boolean close(double first, double second) { 069 double diff = first - second; 070 return Math.abs(diff) < EPSILON; 071 } 072 073 /** Return a new array that is the convolution of the two argument arrays. 074 * The length of the new array is equal to the sum of the lengths of the 075 * two argument arrays minus one. Note that convolution is the same 076 * as polynomial multiplication. If the two argument arrays represent 077 * the coefficients of two polynomials, then the resulting array 078 * represents the coefficients of the product polynomial. 079 * @param array1 The first array. 080 * @param array2 The second array. 081 * @return A new array of doubles. 082 */ 083 public static final double[] convolve(double[] array1, double[] array2) { 084 double[] result; 085 int resultSize = array1.length + array2.length - 1; 086 087 if (resultSize < 0) { 088 // If we attempt to convolve two zero length arrays, return 089 // a zero length array. 090 result = new double[0]; 091 return result; 092 } 093 094 result = new double[resultSize]; 095 096 // The result is assumed initialized to zero. 097 for (int i = 0; i < array1.length; i++) { 098 for (int j = 0; j < array2.length; j++) { 099 result[i + j] += array1[i] * array2[j]; 100 } 101 } 102 103 return result; 104 } 105 106 /** Return a new array that is the convolution of two complex arrays. 107 * The length of the new array is equal to the sum of the lengths of the 108 * two argument arrays minus one. Note that some authors define 109 * complex convolution slightly differently as the convolution of the 110 * first array with the <em>conjugate</em> of the second. If you need 111 * to use that definition, then conjugate the second array before 112 * calling this method. Convolution defined as we do here is the 113 * same as polynomial multiplication. If the two argument arrays 114 * represent the coefficients of two polynomials, then the resulting 115 * array represents the coefficients of the product polynomial. 116 * @param array1 The first array. 117 * @param array2 The second array. 118 * @return A new array of complex numbers. 119 */ 120 public static final Complex[] convolve(Complex[] array1, Complex[] array2) { 121 Complex[] result; 122 int resultSize = array1.length + array2.length - 1; 123 124 if (resultSize < 0) { 125 // If we attempt to convolve two zero length arrays, return 126 // a zero length array. 127 result = new Complex[0]; 128 return result; 129 } 130 131 double[] reals = new double[resultSize]; 132 double[] imags = new double[resultSize]; 133 134 for (int i = 0; i < array1.length; i++) { 135 for (int j = 0; j < array2.length; j++) { 136 reals[i + j] += array1[i].real * array2[j].real 137 - array1[i].imag * array2[j].imag; 138 imags[i + j] += array1[i].imag * array2[j].real 139 + array1[i].real * array2[j].imag; 140 } 141 } 142 143 result = new Complex[resultSize]; 144 145 for (int i = 0; i < result.length; i++) { 146 result[i] = new Complex(reals[i], imags[i]); 147 } 148 149 return result; 150 } 151 152 /** Return a new array of doubles that is the forward, normalized 153 * DCT of the input array of doubles. 154 * This method automatically computes the order of the transform 155 * based on the length of the input array. It is equivalent to 156 * DCT(x, order, DCT_TYPE_NORMALIZED), where 2^order is the smallest 157 * power of two greater than or equal to the length of the specified 158 * array. The length of the result is 2^order. 159 * @param x An array of doubles. 160 * @return A new array of doubles, with length 2^order. 161 */ 162 public static final double[] DCT(double[] x) { 163 return DCT(x, order(x.length), DCT_TYPE_NORMALIZED); 164 } 165 166 /** Return a new array of doubles that is the forward, normalized 167 * DCT of the input array of doubles. 168 * This method is equivalent to 169 * DCT(x, order, DCT_TYPE_NORMALIZED). 170 * @param x An array of doubles. 171 * @param order Log base 2 of the size of the transform. 172 * @return A new array of doubles, with length 2^order. 173 */ 174 public static final double[] DCT(double[] x, int order) { 175 return DCT(x, order, DCT_TYPE_NORMALIZED); 176 } 177 178 /** Return a new array of doubles that is the forward DCT of the 179 * input array of doubles. 180 * See the DCT_TYPE_XXX constants for documentation of the 181 * exact formula, which depends on the type. 182 * @param x An array of doubles. 183 * @param order Log base 2 of the size of the transform. 184 * @param type The type of DCT, which is one of DCT_TYPE_NORMALIZED, 185 * DCT_TYPE_UNNORMALIZED, or DCT_TYPE_ORTHONORMAL. 186 * @see #DCT_TYPE_NORMALIZED 187 * @see #DCT_TYPE_UNNORMALIZED 188 * @see #DCT_TYPE_ORTHONORMAL 189 * @return A new array of doubles, with length 2^order. 190 */ 191 public static final double[] DCT(double[] x, int order, int type) { 192 _checkTransformArgs(x, order, _FORWARD_TRANSFORM); 193 194 if (type >= DCT_TYPES) { 195 throw new IllegalArgumentException( 196 "ptolemy.math.SignalProcessing.DCT(): Unrecognized DCT type"); 197 } 198 199 int size = 1 << order; 200 201 // Make sure the tables have enough entries for the DCT computation 202 if (order > _FFCTGenLimit) { 203 _FFCTTableGen(order); 204 } 205 206 double[] returnValue = _DCT(x, size, order); 207 208 switch (type) { 209 case DCT_TYPE_ORTHONORMAL: 210 211 double factor = Math.sqrt(2.0 / size); 212 returnValue = DoubleArrayMath.scale(returnValue, factor); 213 214 // no break here 215 case DCT_TYPE_NORMALIZED: 216 returnValue[0] *= ExtendedMath.ONE_OVER_SQRT_2; 217 break; 218 } 219 220 return returnValue; 221 } 222 223 /** Return the value of the argument 224 * in decibels, which is defined to be 20*log<sub>10</sub>(<em>z</em>), 225 * where <em>z</em> is the argument. 226 * Note that if the input represents power, which is proportional to a 227 * magnitude squared, then this should be divided 228 * by two to get 10*log<sub>10</sub>(<em>z</em>). 229 * @param value The value to convert to decibels. 230 * @deprecated Use toDecibels() instead. 231 * @see #toDecibels(double) 232 */ 233 @Deprecated 234 public static final double decibel(double value) { 235 return toDecibels(value); 236 } 237 238 /** Return a new array the value of the argument array 239 * in decibels, using the previous decibel() method. 240 * You may wish to combine this with DoubleArrayMath.limit(). 241 * @deprecated Use toDecibels() instead. 242 */ 243 @Deprecated 244 public static final double[] decibel(double[] values) { 245 double[] result = new double[values.length]; 246 247 for (int i = values.length - 1; i >= 0; i--) { 248 result[i] = toDecibels(values[i]); 249 } 250 251 return result; 252 } 253 254 /** Return a new array that is formed by taking every nth sample 255 * starting with the 0th sample, and discarding the rest. 256 * This method calls : 257 * downsample(x, n, 0) 258 * @param x An array of doubles. 259 * @param n An integer specifying the downsampling factor. 260 * @return A new array of doubles of length = floor(L / n), where 261 * L is the size of the input array. 262 */ 263 public static final double[] downsample(double[] x, int n) { 264 return downsample(x, n, 0); 265 } 266 267 /** Return a new array that is formed by taking every nth sample 268 * starting at startIndex, and discarding the samples in between. 269 * @param x An array of doubles. 270 * @param n An integer specifying the downsampling factor. 271 * @param startIndex An integer specifying the index of sample at 272 * which to start downsampling. This integer must be between 0 and 273 * L - 1, where L is the size of the input array. 274 * @return A new array of doubles of length = 275 * floor((L - startIndex) / n), 276 * where L is the size of the input array. 277 */ 278 public static final double[] downsample(double[] x, int n, int startIndex) { 279 if (x.length <= 0) { 280 throw new IllegalArgumentException( 281 "ptolemy.math.SignalProcessing.downsample(): " 282 + "array length must be greater than 0."); 283 } 284 285 if (n <= 0) { 286 throw new IllegalArgumentException( 287 "ptolemy.math.SignalProcessing.downsample(): " 288 + "downsampling factor must be greater than 0."); 289 } 290 291 if (startIndex < 0 || startIndex > x.length - 1) { 292 throw new IllegalArgumentException( 293 "ptolemy.math.SignalProcessing.downsample(): " 294 + "startIndex must be between 0 and L - 1, where L is the " 295 + "size of the input array."); 296 } 297 298 int length = (x.length + 1 - startIndex) / n; 299 double[] returnValue = new double[length]; 300 301 int destIndex; 302 int srcIndex = startIndex; 303 304 for (destIndex = 0; destIndex < length; destIndex++) { 305 returnValue[destIndex] = x[srcIndex]; 306 srcIndex += n; 307 } 308 309 return returnValue; 310 } 311 312 /** Return a new array of complex numbers which is the FFT 313 * of an input array of complex numbers. The order of the transform 314 * is the next power of two greater than the length of the argument. 315 * The input is zero-padded if it does not match this length. 316 * @param x An array of complex numbers. 317 * @return The FFT of the argument. 318 */ 319 public static final Complex[] FFT(Complex[] x) { 320 return FFTComplexOut(x, order(x.length)); 321 } 322 323 /** Return a new array of complex numbers which is the FFT 324 * of an input array of complex numbers. 325 * The input is zero-padded if it does not match the length. 326 * @param x An array of complex numbers. 327 * @param order The log base 2 of the length of the FFT. 328 * @return The FFT of the argument. 329 */ 330 public static final Complex[] FFT(Complex[] x, int order) { 331 return FFTComplexOut(x, order); 332 } 333 334 /** Return a new array of Complex's which is the forward FFT 335 * of an input array of Complex's. 336 * This method automatically computes the order of the transform 337 * based on the length of the input array, and calls 338 * FFTComplexOut(x, order). 339 * @param x An array of Complex's. 340 * @return A new array of Complex's. 341 */ 342 public static final Complex[] FFTComplexOut(Complex[] x) { 343 return FFTComplexOut(x, order(x.length)); 344 } 345 346 /** Return a new array of Complex's which is the forward FFT 347 * of an input array of Complex's. 348 * @param x An array of Complex's. 349 * @param order The base-2 logarithm of the size of the transform. 350 * @return A new array of Complex's. 351 */ 352 public static final Complex[] FFTComplexOut(Complex[] x, int order) { 353 x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM); 354 355 double[] realx = ComplexArrayMath.realParts(x); 356 double[] realrealX = FFTRealOut(realx, order); 357 double[] imagrealX = FFTImagOut(realx, order); 358 359 double[] imagx = ComplexArrayMath.imagParts(x); 360 double[] realimagX = FFTRealOut(imagx, order); 361 double[] imagimagX = FFTImagOut(imagx, order); 362 363 realrealX = DoubleArrayMath.subtract(realrealX, imagimagX); 364 imagrealX = DoubleArrayMath.add(imagrealX, realimagX); 365 366 return ComplexArrayMath.formComplexArray(realrealX, imagrealX); 367 } 368 369 /** Return a new array of Complex's which is the forward FFT 370 * of a real input array of doubles. 371 * This method automatically computes the order of the transform 372 * based on the length of the input array, and calls 373 * FFTComplexOut(x, order). 374 * @param x An array of doubles. 375 * @return A new array of Complex's. 376 */ 377 public static final Complex[] FFTComplexOut(double[] x) { 378 return FFTComplexOut(x, order(x.length)); 379 } 380 381 /** Return a new array of Complex's which is the forward FFT 382 * of a real input array of doubles. 383 * This method is half as expensive as computing the FFT of a 384 * Complex array. 385 * @param x An array of doubles. 386 * @param order The base-2 logarithm of the size of the transform. 387 * @return A new array of Complex's. 388 */ 389 public static final Complex[] FFTComplexOut(double[] x, int order) { 390 // Argument checking is done inside FFTRealOut() and FFTImagOut() 391 double[] realPart = FFTRealOut(x, order); 392 double[] imagPart = FFTImagOut(x, order); 393 394 return ComplexArrayMath.formComplexArray(realPart, imagPart); 395 } 396 397 /** Return a new array of doubles which is the imaginary part of the 398 * FFT of an input array of Complex's. 399 * This method automatically computes the order of the transform 400 * based on the length of the input array, and calls 401 * FFTImagOut(x, order). 402 * @param x An array of Complex's. 403 * @return A new array of doubles. 404 */ 405 public static final double[] FFTImagOut(Complex[] x) { 406 return FFTImagOut(x, order(x.length)); 407 } 408 409 /** Return a new array of doubles which is the imaginary part of the 410 * FFT of an input array of Complex's. 411 * This method is half as expensive as computing both the real and 412 * imaginary parts of an FFT on a array of Complex's. It is especially 413 * useful when the output is known to be purely imaginary. 414 * @param x An array of Complex's. 415 * @param order The base-2 logarithm of the size of the transform. 416 * @return A new array of doubles. 417 */ 418 public static final double[] FFTImagOut(Complex[] x, int order) { 419 x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM); 420 421 double[] realx = ComplexArrayMath.realParts(x); 422 double[] imagrealX = FFTImagOut(realx, order); 423 424 double[] imagx = ComplexArrayMath.imagParts(x); 425 double[] realimagX = FFTRealOut(imagx, order); 426 427 return DoubleArrayMath.add(imagrealX, realimagX); 428 } 429 430 /** Return a new array of doubles that is the imaginary part of the FFT 431 * of the real input array of doubles. 432 * This method is half as expensive as computing both the real and 433 * imaginary parts of a FFT on a real array. It is especially useful when 434 * the output is known to be purely imaginary (input is odd). 435 * This method automatically computes the order of the transform 436 * based on the length of the input array, and calls: 437 * FFTImagOut(x, order) 438 * @param x An array of doubles. 439 * @return A new array of doubles. 440 */ 441 public static final double[] FFTImagOut(double[] x) { 442 return FFTImagOut(x, order(x.length)); 443 } 444 445 /** Return a new array of doubles that is the imaginary part of the FFT 446 * of the real input array of doubles. 447 * This method is half as expensive as computing both the real and 448 * imaginary parts of a FFT on a real array. It is especially useful when 449 * the output is known to be purely imaginary (input is odd). 450 * @param x An array of doubles. 451 * @param order The base-2 logarithm of the size of the transform. 452 * @return A new array of doubles. 453 */ 454 public static final double[] FFTImagOut(double[] x, int order) { 455 x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM); 456 457 int size = 1 << order; 458 int halfN = size >> 1; 459 460 // Make sure the tables have enough entries for the DCT computation 461 // at size N/4 462 if (order - 2 > _FFCTGenLimit) { 463 _FFCTTableGen(order - 2); 464 } 465 466 double[] imagPart = _sinDFT(x, size, order); 467 468 double[] returnValue = new double[size]; 469 470 // Don't bother to look at the array for element 0 471 // returnValue[0] = 0.0; // not necessary in Java 472 for (int k = 1; k < halfN; k++) { 473 returnValue[k] = -imagPart[k]; 474 returnValue[size - k] = imagPart[k]; 475 } 476 477 // returnValue[halfN] = 0.0; // not necessary in Java 478 return returnValue; 479 } 480 481 /** Return a new array of doubles which is the real part of the 482 * forward FFT of an input array of Complex's. 483 * This method automatically computes the order of the transform 484 * based on the length of the input array, and calls : 485 * FFTRealOut(x, order) 486 * @param x An array of Complex's. 487 * @return A new array of doubles. 488 */ 489 public static final double[] FFTRealOut(Complex[] x) { 490 return FFTRealOut(x, order(x.length)); 491 } 492 493 /** Return a new array of doubles which is the real part of the 494 * forward FFT of an input array of Complex's. 495 * This method is half as expensive as computing both the real and 496 * imaginary parts of an FFT on a array of Complex's. It is especially 497 * useful when the output is known to be purely real. 498 * @param x An array of Complex's. 499 * @param order The base-2 logarithm of the size of the transform. 500 * @return A new array of doubles. 501 */ 502 public static final double[] FFTRealOut(Complex[] x, int order) { 503 x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM); 504 505 double[] realx = ComplexArrayMath.realParts(x); 506 double[] realrealX = FFTRealOut(realx, order); 507 508 double[] imagx = ComplexArrayMath.imagParts(x); 509 double[] imagimagX = FFTImagOut(imagx, order); 510 511 return DoubleArrayMath.subtract(realrealX, imagimagX); 512 } 513 514 /** Return a new array of doubles that is the real part of the FFT of 515 * the real input array of doubles. 516 * This method automatically computes the order of the transform 517 * based on the length of the input array, and calls : 518 * FFTRealOut(x, order). 519 * @param x An array of doubles. 520 * @return A new array of doubles. 521 */ 522 public static final double[] FFTRealOut(double[] x) { 523 return FFTRealOut(x, order(x.length)); 524 } 525 526 /** Return a new array of doubles that is the real part of the FFT of 527 * the real input array of doubles. 528 * This method is half as expensive as computing both the real and 529 * imaginary parts of an FFT on a real array. It is especially useful 530 * when the output is known to be purely real (input is even). 531 * @param x An array of doubles. 532 * @param order The base-2 logarithm of the size of the transform 533 * @return A new array of doubles. 534 */ 535 public static final double[] FFTRealOut(double[] x, int order) { 536 x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM); 537 538 int size = 1 << order; 539 int halfN = size >> 1; 540 541 if (x.length < size) { 542 x = DoubleArrayMath.resize(x, size); 543 } 544 545 // Make sure the tables have enough entries for the DCT computation 546 // at size N/4 547 if (order - 2 > _FFCTGenLimit) { 548 _FFCTTableGen(order - 2); 549 } 550 551 double[] realPart = _cosDFT(x, size, order); 552 double[] returnValue = new double[size]; 553 554 System.arraycopy(realPart, 0, returnValue, 0, halfN + 1); 555 556 for (int k = halfN + 1; k < size; k++) { 557 returnValue[k] = realPart[size - k]; 558 } 559 560 return returnValue; 561 } 562 563 /** Return a new array of doubles that is the inverse, normalized 564 * DCT of the input array of doubles. 565 * This method automatically computes the order of the transform 566 * based on the length of the input array. It is equivalent to 567 * IDCT(x, order, DCT_TYPE_NORMALIZED), where 2^order is the 568 * next power of two larger than or equal to the length of the 569 * specified array. The returned array has length 2^order. 570 * @param x An array of doubles. 571 * @return A new array of doubles with length 2^order. 572 */ 573 public static final double[] IDCT(double[] x) { 574 return IDCT(x, order(x.length), DCT_TYPE_NORMALIZED); 575 } 576 577 /** Return a new array of doubles that is the inverse, normalized 578 * DCT of the input array of doubles, using the specified order. 579 * The length of the DCT is 2^<i>order</i>. This is equivalent to 580 * IDCT(x, order, DCT_TYPE_NORMALIZED). 581 * @param x An array of doubles. 582 * @return A new array of doubles with length 2^order. 583 */ 584 public static final double[] IDCT(double[] x, int order) { 585 return IDCT(x, order, DCT_TYPE_NORMALIZED); 586 } 587 588 /** Return a new array of doubles that is the inverse DCT of the 589 * input array of doubles. 590 * See the DCT_TYPE_XXX constants for documentation of the 591 * exact formula, which depends on the type. 592 * @param x An array of doubles. 593 * @param order The base-2 logarithm of the size of the transform. 594 * @param type The type of IDCT, which is one of DCT_TYPE_NORMALIZED, 595 * DCT_TYPE_UNNORMALIZED, or DCT_TYPE_ORTHONORMAL. 596 * @see #DCT_TYPE_NORMALIZED 597 * @see #DCT_TYPE_UNNORMALIZED 598 * @see #DCT_TYPE_ORTHONORMAL 599 * @return A new array of doubles with length 2^order. 600 */ 601 public static final double[] IDCT(double[] x, int order, int type) { 602 // check if order > 31 603 if (type >= DCT_TYPES) { 604 throw new IllegalArgumentException( 605 "ptolemy.math.SignalProcessing.IDCT() : Bad DCT type"); 606 } 607 608 int size = 1 << order; 609 int twoSize = 2 << order; 610 611 // Generate scaleFactors if necessary 612 if (_IDCTfactors[type][order] == null) { 613 _IDCTfactors[type][order] = new Complex[twoSize]; 614 615 double oneOverTwoSize = 1.0 / twoSize; 616 617 double factor = 1.0; 618 double oneOverE0 = 2.0; 619 620 switch (type) { 621 case DCT_TYPE_NORMALIZED: 622 factor = 2.0; 623 oneOverE0 = ExtendedMath.SQRT_2; 624 break; 625 626 case DCT_TYPE_ORTHONORMAL: 627 factor = Math.sqrt(twoSize); // == 2 * sqrt(N/2) 628 oneOverE0 = ExtendedMath.SQRT_2; 629 break; 630 631 case DCT_TYPE_UNNORMALIZED: 632 factor = 2.0; 633 oneOverE0 = 1.0; 634 break; 635 } 636 637 _IDCTfactors[type][order][0] = new Complex(oneOverE0 * factor, 0.0); 638 639 for (int k = 1; k < twoSize; k++) { 640 Complex c = new Complex(0, k * Math.PI * oneOverTwoSize); 641 _IDCTfactors[type][order][k] = c.exp().scale(factor); 642 } 643 } 644 645 Complex[] evenX = new Complex[twoSize]; 646 Complex[] myFactors = _IDCTfactors[type][order]; 647 648 // Convert to Complex, while multiplying by scaleFactors 649 evenX[0] = myFactors[0].scale(x[0]); 650 651 for (int k = 1; k < size; k++) { 652 // Do zero-padding here 653 if (k >= x.length) { 654 evenX[k] = new Complex(0.0); 655 evenX[twoSize - k] = new Complex(0.0); 656 } else { 657 evenX[k] = myFactors[k].scale(x[k]); 658 evenX[twoSize - k] = myFactors[twoSize - k].scale(-x[k]); 659 } 660 } 661 662 evenX[size] = new Complex(0.0, 0.0); 663 664 double[] longOutput = IFFTRealOut(evenX, order + 1); 665 666 // Truncate result 667 return DoubleArrayMath.resize(longOutput, size); 668 } 669 670 /** Return a new array of complex numbers which is the inverse FFT 671 * of an input array of complex numbers. The length of the result 672 * is the next power of two greater than the length of the argument. 673 * The input is zero-padded if it does not match this length. 674 * @param x An array of complex numbers. 675 * @return The inverse FFT of the argument. 676 */ 677 public static final Complex[] IFFT(Complex[] x) { 678 return IFFTComplexOut(x, order(x.length)); 679 } 680 681 /** Return a new array of complex numbers which is the inverse FFT 682 * of an input array of complex numbers. 683 * The input is zero-padded if it does not match this length. 684 * @param x An array of complex numbers. 685 * @param order The log base 2 of the length of the FFT. 686 * @return The inverse FFT of the argument. 687 */ 688 public static final Complex[] IFFT(Complex[] x, int order) { 689 return IFFTComplexOut(x, order); 690 } 691 692 /** Return a new array of Complex's which is the inverse FFT 693 * of an input array of Complex's. 694 * This method automatically computes the order of the transform 695 * based on the length of the input array. 696 * @param x An array of Complex's. 697 * @return A new array of Complex's. 698 */ 699 public static final Complex[] IFFTComplexOut(Complex[] x) { 700 return IFFTComplexOut(x, order(x.length)); 701 } 702 703 /** Return a new array of Complex's which is the forward FFT 704 * of an input array of Complex's. 705 * @param x An array of Complex's. 706 * @param order The base-2 logarithm of the size of the transform. 707 * @return A new array of Complex's. 708 */ 709 public static final Complex[] IFFTComplexOut(Complex[] x, int order) { 710 x = _checkTransformArgs(x, order, _INVERSE_TRANSFORM); 711 712 Complex[] conjX = ComplexArrayMath.conjugate(x); 713 Complex[] yConj = FFTComplexOut(conjX, order); 714 Complex[] y = ComplexArrayMath.conjugate(yConj); 715 716 // scale by 1/N 717 double oneOverN = 1.0 / (1 << order); 718 return ComplexArrayMath.scale(y, oneOverN); 719 } 720 721 /** Return a new array of doubles which is the real part of the inverse 722 * FFT of an input array of Complex's. 723 * This is less than half as expensive as computing both the real and 724 * imaginary parts. It is especially useful when it is known that the 725 * output is purely real. 726 * This method automatically computes the order of the transform 727 * based on the length of the input array, and calls : 728 * IFFTRealOut(x, order) 729 * @param x An array of Complex's. 730 * @return A new array of doubles. 731 */ 732 public static final double[] IFFTRealOut(Complex[] x) { 733 return IFFTRealOut(x, order(x.length)); 734 } 735 736 /** Return a new array of doubles which is the real part of the inverse 737 * FFT of an input array of Complex's. 738 * This method is less than half as expensive as computing both the 739 * real and imaginary parts of an IFFT of an array of Complex's. It is 740 * especially useful when it is known that the output is purely real. 741 * @param x An array of Complex's. 742 * @return A new array of doubles. 743 */ 744 public static final double[] IFFTRealOut(Complex[] x, int order) { 745 x = _checkTransformArgs(x, order, _INVERSE_TRANSFORM); 746 747 double[] realx = ComplexArrayMath.realParts(x); 748 double[] realrealX = FFTRealOut(realx, order); 749 750 double[] imagx = ComplexArrayMath.imagParts(x); 751 double[] imagimagX = FFTImagOut(imagx, order); 752 753 realrealX = DoubleArrayMath.add(realrealX, imagimagX); 754 755 // scale by 1/N 756 double oneOverN = 1.0 / (1 << order); 757 return DoubleArrayMath.scale(realrealX, oneOverN); 758 } 759 760 /** Return a new array of doubles which is the real part of the inverse 761 * FFT of an input array of doubles. 762 * This method automatically computes the order of the transform 763 * based on the length of the input array, and calls : 764 * IFFTRealOut(x, order) 765 * @param x An array of doubles. 766 * @return A new array of doubles. 767 */ 768 public static double[] IFFTRealOut(double[] x) { 769 return IFFTRealOut(x, order(x.length)); 770 } 771 772 /** Return a new array of doubles which is the real part of the inverse 773 * FFT of an input array of doubles. This method is less than half 774 * as expensive as computing the real part of an IFFT of an array of 775 * Complex's. It is especially useful when both the input and output 776 * are known to be purely real. 777 * @param x An array of doubles. 778 * @param order The base-2 logarithm of the size of the transform. 779 * @return A new array of doubles. 780 */ 781 public static double[] IFFTRealOut(double[] x, int order) { 782 double[] y = FFTRealOut(x, order); 783 784 // scale by 1/N 785 double oneOverN = 1.0 / (1 << order); 786 return DoubleArrayMath.scale(y, oneOverN); 787 } 788 789 /** Return a new array that is filled with samples of a Bartlett 790 * window of a specified length. Throw an IllegalArgumentException 791 * if the length is less than 1 or the window type is unknown. 792 * @param length The length of the window to be generated. 793 * @return A new array of doubles. 794 */ 795 public static final double[] generateBartlettWindow(int length) { 796 if (length < 1) { 797 throw new IllegalArgumentException("ptolemy.math.SignalProcessing" 798 + ".generateBartlettWindow(): " 799 + " length of window should be greater than 0."); 800 } 801 802 int M = length - 1; 803 int n; 804 double[] window = new double[length]; 805 806 int halfM = M / 2; 807 double twoOverM = 2.0 / M; 808 809 for (n = 0; n <= halfM; n++) { 810 window[n] = n * twoOverM; 811 } 812 813 for (n = halfM + 1; n < length; n++) { 814 window[n] = 2.0 - n * twoOverM; 815 } 816 817 return window; 818 } 819 820 /** Return a new array that is filled with samples of a Blackman 821 * window of a specified length. Throw an IllegalArgumentException 822 * if the length is less than 1 or the window type is unknown. 823 * @param length The length of the window to be generated. 824 * @return A new array of doubles. 825 */ 826 public static final double[] generateBlackmanWindow(int length) { 827 if (length < 1) { 828 throw new IllegalArgumentException("ptolemy.math.SignalProcessing" 829 + ".generateBlackmanWindow(): " 830 + " length of window should be greater than 0."); 831 } 832 833 int M = length - 1; 834 int n; 835 double[] window = new double[length]; 836 837 double twoPiOverM = 2.0 * Math.PI / M; 838 double fourPiOverM = 2.0 * twoPiOverM; 839 840 for (n = 0; n < length; n++) { 841 window[n] = 0.42 - 0.5 * Math.cos(twoPiOverM * n) 842 + 0.08 * Math.cos(fourPiOverM * n); 843 } 844 845 return window; 846 } 847 848 /** Return a new array that is filled with samples of a Blackman Harris 849 * window of a specified length. Throw an IllegalArgumentException 850 * if the length is less than 1 or the window type is unknown. 851 * @param length The length of the window to be generated. 852 * @return A new array of doubles. 853 */ 854 public static final double[] generateBlackmanHarrisWindow(int length) { 855 if (length < 1) { 856 throw new IllegalArgumentException("ptolemy.math.SignalProcessing" 857 + ".generateBlackmanHarrisWindow(): " 858 + " length of window should be greater than 0."); 859 } 860 861 int M = length - 1; 862 int n; 863 double[] window = new double[length]; 864 865 double twoPiOverM = 2.0 * Math.PI / M; 866 double fourPiOverM = 2.0 * twoPiOverM; 867 double sixPiOverM = 3.0 * twoPiOverM; 868 869 for (n = 0; n < length; n++) { 870 window[n] = 0.35875 - 0.48829 * Math.cos(twoPiOverM * n) 871 + 0.14128 * Math.cos(fourPiOverM * n) 872 - 0.01168 * Math.cos(sixPiOverM * n); 873 } 874 875 return window; 876 } 877 878 /** Return an array with samples the Gaussian curve (the "bell curve"). 879 * The returned array is symmetric. E.g., to get a Gaussian curve 880 * that extends out to "four sigma," then the <i>extent</i> argument 881 * should be 4.0. 882 * @param standardDeviation The standard deviation. 883 * @param extent The multiple of the standard deviation out to 884 * which the curve is plotted. 885 * @param length The length of the returned array. 886 * @return An array that contains samples of the Gaussian curve. 887 */ 888 public static final double[] generateGaussianCurve(double standardDeviation, 889 double extent, int length) { 890 GaussianSampleGenerator generator = new GaussianSampleGenerator(0.0, 891 standardDeviation); 892 return sampleWave(length, -extent * standardDeviation, 893 2.0 * extent * standardDeviation / length, generator); 894 } 895 896 /** Return a new array that is filled with samples of a Hamming 897 * window of a specified length. Throw an IllegalArgumentException 898 * if the length is less than 1 or the window type is unknown. 899 * @param length The length of the window to be generated. 900 * @return A new array of doubles. 901 */ 902 public static final double[] generateHammingWindow(int length) { 903 if (length < 1) { 904 throw new IllegalArgumentException( 905 "ptolemy.math.SignalProcessing.generateHammingWindow(): " 906 + " length of window should be greater than 0."); 907 } 908 909 int M = length - 1; 910 int n; 911 double[] window = new double[length]; 912 913 double twoPiOverM = 2.0 * Math.PI / M; 914 915 for (n = 0; n < length; n++) { 916 window[n] = 0.54 - 0.46 * Math.cos(twoPiOverM * n); 917 } 918 919 return window; 920 } 921 922 /** Return a new array that is filled with samples of a Hanning 923 * window of a specified length. Throw an IllegalArgumentException 924 * if the length is less than 1 or the window type is unknown. 925 * @param length The length of the window to be generated. 926 * @return A new array of doubles. 927 */ 928 public static final double[] generateHanningWindow(int length) { 929 if (length < 1) { 930 throw new IllegalArgumentException( 931 "ptolemy.math.SignalProcessing.generateHanningWindow(): " 932 + " length of window should be greater than 0."); 933 } 934 935 int M = length - 1; 936 int n; 937 double[] window = new double[length]; 938 939 double twoPiOverM = 2.0 * Math.PI / M; 940 941 for (n = 0; n < length; n++) { 942 window[n] = 0.5 - 0.5 * Math.cos(twoPiOverM * n); 943 } 944 945 return window; 946 } 947 948 /** Return an array with samples a polynomial curve. 949 * The first argument is an array giving the coefficients 950 * of the polynomial, starting with the constant term, followed 951 * by the linear term, followed by the quadratic term, etc. 952 * The remaining coefficients determine the points at which 953 * the polynomial curve is sampled. That is, they determine 954 * the values of the polynomial variable at which the polynomial 955 * is evaluated. 956 * @param polynomial An array with polynomial coefficients. 957 * @param start The point of the first sample. 958 * @param step The step size between samples. 959 * @param length The length of the returned array. 960 * @return An array that contains samples of a polynomial curve. 961 */ 962 public static final double[] generatePolynomialCurve(double[] polynomial, 963 double start, double step, int length) { 964 PolynomialSampleGenerator generator = new PolynomialSampleGenerator( 965 polynomial, 1); 966 return sampleWave(length, start, step, generator); 967 } 968 969 /** Return an array containing a symmetric raised-cosine pulse. 970 * This pulse is widely used in communication systems, and is called 971 * a "raised cosine pulse" because the magnitude its Fourier transform 972 * has a shape that ranges from rectangular (if the excess bandwidth 973 * is zero) to a cosine curved that has been raised to be non-negative 974 * (for excess bandwidth of 1.0). The elements of the returned array 975 * are samples of the function: 976 * <pre> 977 * sin(PI t/T) cos(x PI t/T) 978 * h(t) = ----------- * ----------------- 979 * PI t/T 1-(2 x t/T)<sup>2</sup> 980 * </pre> 981 * where x is the excess bandwidth and T is the number of samples 982 * from the center of the pulse to the first zero crossing. 983 * The samples are taken with a 984 * sampling interval of 1.0, and the returned array is symmetric. 985 * With an excessBandwidth of 0.0, this pulse is a sinc pulse. 986 * @param excessBandwidth The excess bandwidth. 987 * @param firstZeroCrossing The number of samples from the center of the 988 * pulse to the first zero crossing. 989 * @param length The length of the returned array. 990 * @return An array containing a symmetric raised-cosine pulse. 991 */ 992 public static final double[] generateRaisedCosinePulse( 993 double excessBandwidth, double firstZeroCrossing, int length) { 994 RaisedCosineSampleGenerator generator = new RaisedCosineSampleGenerator( 995 firstZeroCrossing, excessBandwidth); 996 return sampleWave(length, -(length - 1) / 2.0, 1.0, generator); 997 } 998 999 /** Return a new array that is filled with samples of a rectangular 1000 * window of a specified length. Throw an IllegalArgumentException 1001 * if the length is less than 1 or the window type is unknown. 1002 * @param length The length of the window to be generated. 1003 * @return A new array of doubles. 1004 */ 1005 public static final double[] generateRectangularWindow(int length) { 1006 if (length < 1) { 1007 throw new IllegalArgumentException("ptolemy.math.SignalProcessing" 1008 + ".generateRectangularWindow(): " 1009 + " length of window should be greater than 0."); 1010 } 1011 1012 int n; 1013 double[] window = new double[length]; 1014 1015 for (n = 0; n < length; n++) { 1016 window[n] = 1.0; 1017 } 1018 1019 return window; 1020 } 1021 1022 /** Return an array containing a symmetric raised-cosine pulse. 1023 * This pulse is widely used in communication systems, and is called 1024 * a "raised cosine pulse" because the magnitude its Fourier transform 1025 * has a shape that ranges from rectangular (if the excess bandwidth 1026 * is zero) to a cosine curved that has been raised to be non-negative 1027 * (for excess bandwidth of 1.0). The elements of the returned array 1028 * are samples of the function: 1029 * <pre> 1030 * 4 x(cos((1+x)PI t/T) + T sin((1-x)PI t/T)/(4x t/T)) 1031 * h(t) = --------------------------------------------------- 1032 * PI sqrt(T)(1-(4 x t/T)<sup>2</sup>) 1033 * </pre> 1034 * <p> 1035 * where <i>x</i> is the the excess bandwidth. 1036 * This pulse convolved with itself will, in principle, be equal 1037 * to a raised cosine pulse. However, because the pulse decays rather 1038 * slowly for low excess bandwidth, this ideal is not 1039 * closely approximated by short finite approximations of the pulse. 1040 * The samples are taken with a 1041 * sampling interval of 1.0, and the returned array is symmetric. 1042 * With an excessBandwidth of 0.0, this pulse is a scaled sinc pulse. 1043 * @param excessBandwidth The excess bandwidth. 1044 * @param firstZeroCrossing The number of samples from the center of the 1045 * pulse to the first zero crossing. 1046 * @param length The length of the returned array. 1047 * @return A new array containing a square-root raised-cosine pulse. 1048 */ 1049 public static final double[] generateSqrtRaisedCosinePulse( 1050 double excessBandwidth, double firstZeroCrossing, int length) { 1051 RaisedCosineSampleGenerator generator = new RaisedCosineSampleGenerator( 1052 firstZeroCrossing, excessBandwidth); 1053 return sampleWave(length, -(length - 1) / 2.0, 1.0, generator); 1054 } 1055 1056 /** Return a new array that is filled with samples of a window of a 1057 * specified length and type. Throw an IllegalArgumentException 1058 * if the length is less than 1 or the window type is unknown. 1059 * @param length The length of the window to be generated. 1060 * @param windowType The type of window to generate. 1061 * @return A new array of doubles. 1062 */ 1063 public static final double[] generateWindow(int length, int windowType) { 1064 if (length < 1) { 1065 throw new IllegalArgumentException( 1066 "ptolemy.math.SignalProcessing.generateWindow(): " 1067 + " length of window should be greater than 0."); 1068 } 1069 1070 switch (windowType) { 1071 case WINDOW_TYPE_RECTANGULAR: 1072 return generateRectangularWindow(length); 1073 1074 case WINDOW_TYPE_BARTLETT: 1075 return generateBartlettWindow(length); 1076 1077 case WINDOW_TYPE_HANNING: 1078 return generateHanningWindow(length); 1079 1080 case WINDOW_TYPE_HAMMING: 1081 return generateHammingWindow(length); 1082 1083 case WINDOW_TYPE_BLACKMAN: 1084 return generateBlackmanWindow(length); 1085 1086 case WINDOW_TYPE_BLACKMAN_HARRIS: 1087 return generateBlackmanHarrisWindow(length); 1088 1089 default: 1090 throw new IllegalArgumentException( 1091 "ptolemy.math.SignalProcessing.generateWindow(): " 1092 + "Unknown window type (" + windowType + ")."); 1093 } 1094 } 1095 1096 /** Return the next power of two larger than the argument. 1097 * @param x A positive real number. 1098 * @exception IllegalArgumentException If the argument is less than 1099 * or equal to zero. 1100 */ 1101 public static final int nextPowerOfTwo(double x) { 1102 if (x <= 0.0) { 1103 throw new IllegalArgumentException( 1104 "ptolemy.math.SignalProcessing.nextPowerOfTwo(): " 1105 + "argument (" + x + ") is not a positive number."); 1106 } 1107 1108 double m = Math.log(x) * _LOG2SCALE; 1109 int exp = (int) Math.ceil(m); 1110 return 1 << exp; 1111 } 1112 1113 /** Return the "order" of a transform size, i.e. the base-2 logarithm 1114 * of the size. The order will be rounded up to the nearest integer. 1115 * If the size is zero or negative, throw an IllegalArgumentException. 1116 * @param size The size of the transform. 1117 * @return The order of the transform. 1118 */ 1119 public static final int order(int size) { 1120 if (size <= 0) { 1121 throw new IllegalArgumentException("ptolemy.math.SignalProcessing:" 1122 + " size of transform must be positive."); 1123 } 1124 1125 double m = Math.log(size) * _LOG2SCALE; 1126 double exp = Math.ceil(m); 1127 return (int) exp; 1128 } 1129 1130 /** Given an array of pole locations, an array of zero locations, and a 1131 * gain term, return frequency response specified by these. 1132 * This is calculated by walking around the unit circle and forming 1133 * the product of the distances to the zeros, dividing by the product 1134 * of the distances to the poles, and multiplying by the gain. 1135 * The length of the returned array is <i>numSteps</i>. 1136 * 1137 * @param poles An array of pole locations. 1138 * @param zeros An array of zero locations. 1139 * @param gain A complex gain. 1140 * @param numSteps The number of samples in the returned 1141 * frequency response. 1142 */ 1143 public static final Complex[] poleZeroToFrequency(Complex[] poles, 1144 Complex[] zeros, Complex gain, int numSteps) { 1145 double step = 2 * Math.PI / numSteps; 1146 Complex[] freq = new Complex[numSteps]; 1147 1148 double angle = -Math.PI; 1149 1150 for (int index = 0; index < freq.length; index++) { 1151 Complex polesContribution = Complex.ONE; 1152 Complex zerosContribution = Complex.ONE; 1153 Complex ejw = new Complex(Math.cos(angle), Math.sin(angle)); 1154 1155 if (poles.length > 0) { 1156 Complex[] diffPoles = ComplexArrayMath.subtract(poles, ejw); 1157 polesContribution = ComplexArrayMath.product(diffPoles); 1158 } 1159 1160 if (zeros.length > 0) { 1161 Complex[] diffZeros = ComplexArrayMath.subtract(zeros, ejw); 1162 zerosContribution = ComplexArrayMath.product(diffZeros); 1163 } 1164 1165 freq[index] = zerosContribution.divide(polesContribution); 1166 freq[index] = freq[index].multiply(gain); 1167 angle += step; 1168 } 1169 1170 return freq; 1171 } 1172 1173 /** Return a new array that is filled with samples of a waveform of a 1174 * specified length. The waveform is sampled with starting at startTime, 1175 * at a sampling period of interval. 1176 * @param length The number of samples to generate. 1177 * @param startTime The corresponding time for the first sample. 1178 * @param interval The time between successive samples. This may 1179 * be negative if the waveform is to be reversed, or zero if the 1180 * array is to be filled with a constant. 1181 * @param sampleGen A DoubleUnaryOperation. 1182 * @return A new array of doubles. 1183 * @see ptolemy.math.DoubleUnaryOperation 1184 */ 1185 public static final double[] sampleWave(int length, double startTime, 1186 double interval, DoubleUnaryOperation sampleGen) { 1187 double time = startTime; 1188 1189 double[] returnValue = new double[length]; 1190 1191 for (int t = 0; t < length; t++) { 1192 returnValue[t] = sampleGen.operate(time); 1193 time += interval; 1194 } 1195 1196 return returnValue; 1197 } 1198 1199 /** Return a sample of a sawtooth wave with the specified period and 1200 * phase at the specified time. The returned value ranges between 1201 * -1.0 and 1.0. The phase is given as a fraction of a cycle, 1202 * typically ranging from 0.0 to 1.0. If the phase is 0.0 or 1.0, 1203 * the wave begins at zero with a rising slope. If it is 0.5, it 1204 * begins at the falling edge with value -1.0. 1205 * If it is 0.25, it begins at +0.5. 1206 * 1207 * Throw an exception if the period is less than or equal to 0. 1208 * 1209 * @param period The period of the sawtooth wave. 1210 * @param phase The phase of the sawtooth wave. 1211 * @param time The time of the sample. 1212 * @return A double in the range -1.0 to +1.0. 1213 */ 1214 public static double sawtooth(double period, double phase, double time) { 1215 if (period <= 0) { 1216 throw new IllegalArgumentException( 1217 "ptolemy.math.SignalProcessing.sawtooth(): " 1218 + "period should be greater than 0."); 1219 } 1220 1221 double point = 2 / period 1222 * Math.IEEEremainder(time + phase * period, period); 1223 1224 // get rid of negative zero 1225 point = point == -0.0 ? 0.0 : point; 1226 1227 // hole at +1.0 1228 return point == 1.0 ? -1.0 : point; 1229 } 1230 1231 /** Return sin(x)/x, the so-called sinc function. 1232 * If the argument is very close to zero, significant quantization 1233 * errors may result (exactly 0.0 is OK, since this just returns 1.0). 1234 * 1235 * @param x A number. 1236 * @return The sinc function. 1237 */ 1238 public static final double sinc(double x) { 1239 if (x == 0.0) { 1240 return 1.0; 1241 } 1242 1243 return Math.sin(x) / x; 1244 } 1245 1246 /** Return a sample of a square wave with the specified period and 1247 * phase at the specified time. The returned value is 1 or -1. 1248 * A sample that falls on the rising edge of the square wave is 1249 * assigned value +1. A sample that falls on the falling edge is 1250 * assigned value -1. The phase is given as a fraction of a 1251 * cycle, typically ranging from 0.0 to 1.0. If the phase is 0.0 1252 * or 1.0, the square wave begins at the start of the +1.0 phase. 1253 * If it is 0.5, it begins at the start of the -1.0 phase. If it 1254 * is 0.25, it begins halfway through the +1.0 portion of the 1255 * wave. 1256 * 1257 * Throw an exception if the period is less than or equal to 0. 1258 * 1259 * @param period The period of the square wave. 1260 * @param phase The phase of the square wave. 1261 * @param time The time of the sample. 1262 * @return +1.0 or -1.0. 1263 */ 1264 public static double square(double period, double phase, double time) { 1265 if (period <= 0) { 1266 throw new IllegalArgumentException( 1267 "ptolemy.math.SignalProcessing.square(): " 1268 + "period should be greater than 0."); 1269 } 1270 1271 double point = 2 / period 1272 * Math.IEEEremainder(time + phase * period, period); 1273 1274 // hole at +1.0 1275 return point >= 0 && point < 1 ? 1.0 : -1.0; 1276 } 1277 1278 /** Return a sample of a triangle wave with the specified period and 1279 * phase at the specified time. The returned value ranges between 1280 * -1.0 and 1.0. The phase is given as a fraction of a cycle, 1281 * typically ranging from 0.0 to 1.0. If the phase is 0.0 or 1.0, 1282 * the wave begins at zero with a rising slope. If it is 0.5, it 1283 * begins at zero with a falling slope. If it is 0.25, it begins at +1.0. 1284 * 1285 * Throw an exception if the period is less than or equal to 0. 1286 * 1287 * @param period The period of the triangle wave. 1288 * @param phase The phase of the triangle wave. 1289 * @param time The time of the sample. 1290 * @return A number in the range -1.0 to +1.0. 1291 */ 1292 public static double triangle(double period, double phase, double time) { 1293 if (period <= 0) { 1294 throw new IllegalArgumentException( 1295 "ptolemy.math.SignalProcessing.triangle(): " 1296 + "period should be greater than 0."); 1297 } 1298 1299 double point = Math.IEEEremainder(time + phase * period, period); 1300 1301 if (-period / 2.0 <= point && point < -period / 4.0) { 1302 point = -(4.0 / period * point) - 2; 1303 } else if (-period / 4.0 <= point && point < period / 4.0) { 1304 point = 4.0 / period * point; 1305 } else { // if (T/4.0 <= point && point < T/2.0) 1306 point = -(4.0 / period * point) + 2; 1307 } 1308 1309 return point; 1310 } 1311 1312 /** Return the value of the argument 1313 * in decibels, which is defined to be 20*log<sub>10</sub>(<em>z</em>), 1314 * where <em>z</em> is the argument. 1315 * Note that if the input represents power, which is proportional to a 1316 * magnitude squared, then this should be divided 1317 * by two to get 10*log<sub>10</sub>(<em>z</em>). 1318 * @param value The value to convert to decibels. 1319 */ 1320 public static final double toDecibels(double value) { 1321 return 20.0 * Math.log(value) * _LOG10SCALE; 1322 } 1323 1324 /** Return a new array that is constructed from the specified 1325 * array by unwrapping the angles. That is, if 1326 * the difference between successive values is greater than 1327 * <em>PI</em> in magnitude, then the second value is modified by 1328 * multiples of 2<em>PI</em> until the difference is less than 1329 * or equal to <em>PI</em>. 1330 * In addition, the first element is modified so that its 1331 * difference from zero is less than or equal to <em>PI</em> in 1332 * magnitude. This method is used for generating more meaningful 1333 * phase plots. 1334 * @param angles An array of angles. 1335 * @return A new array of phase-unwrapped angles. 1336 */ 1337 public static final double[] unwrap(double[] angles) { 1338 double previous = 0.0; 1339 double[] result = new double[angles.length]; 1340 1341 for (int i = 0; i < angles.length; i++) { 1342 result[i] = angles[i]; 1343 1344 while (result[i] - previous < -Math.PI) { 1345 result[i] += 2 * Math.PI; 1346 } 1347 1348 while (result[i] - previous > Math.PI) { 1349 result[i] -= 2 * Math.PI; 1350 } 1351 1352 previous = result[i]; 1353 } 1354 1355 return result; 1356 } 1357 1358 /** Return a new array that is the result of inserting (n-1) zeroes 1359 * between each successive sample in the input array, resulting in an 1360 * array of length n * L, where L is the length of the original array. 1361 * Throw an exception for n ≤ 0. 1362 * @param x The input array of doubles. 1363 * @param n An integer specifying the upsampling factor. 1364 * @return A new array of doubles. 1365 * @exception IllegalArgumentException If the second argument is not 1366 * strictly positive. 1367 */ 1368 public static final double[] upsample(double[] x, int n) { 1369 if (n <= 0) { 1370 throw new IllegalArgumentException( 1371 "ptolemy.math.SignalProcessing.upsample(): " 1372 + "upsampling factor must be greater than or equal to 0."); 1373 } 1374 1375 int length = x.length * n; 1376 double[] returnValue = new double[length]; 1377 int srcIndex = 0; 1378 int destIndex; 1379 1380 // Assume returnValue has been zeroed out 1381 for (destIndex = 0; destIndex < length; destIndex += n) { 1382 returnValue[destIndex] = x[srcIndex]; 1383 srcIndex++; 1384 } 1385 1386 return returnValue; 1387 } 1388 1389 /////////////////////////////////////////////////////////////////// 1390 //// public variables //// 1391 1392 /** A small number ( = 1.0e-9). This number is used by algorithms to 1393 * detect whether a double is close to zero. 1394 */ 1395 public static final double EPSILON = 1.0e-9; 1396 1397 // Scale factor types for the DCT/IDCT 1398 // In the following formulas, 1399 // e[0] = 1/sqrt(2) 1400 // e[k] = 1 for k != 0 1401 1402 /** To select the forward transform : 1403 * <p> 1404 * N - 1 <br> 1405 * X[k] = e[k] sum x[n] * cos ((2n + 1)k * PI / 2N) <br> 1406 * n = 0 <br> 1407 * </p> 1408 * <p> 1409 * and the inverse transform : 1410 * N - 1 <br> 1411 * x(n) = (2/N) sum e(k) X[k] * cos ((2n + 1)k * PI / 2N)<br> 1412 * k = 0 <br> 1413 * </p> 1414 * use this DCT type. 1415 */ 1416 public static final int DCT_TYPE_NORMALIZED = 0; 1417 1418 /** To select the forward transform : 1419 * N - 1 1420 * X[k] = sum x[n] * cos ((2n + 1)k * PI / 2N) 1421 * n = 0 1422 * and the inverse transform : 1423 * N - 1 1424 * x[n] = sum X[k] * cos ((2n + 1)k * PI / 2N) 1425 * k = 0 1426 * use this DCT type. 1427 * This is the definition of the DCT used by MPEG. 1428 */ 1429 public static final int DCT_TYPE_UNNORMALIZED = 1; 1430 1431 /** To select the forward transform : 1432 * N - 1 1433 * X[k] = sqrt(2/N) e[k] sum x[n] * cos ((2n + 1)k * PI / 2N) 1434 * n = 0 1435 * and the inverse transform : 1436 * N - 1 1437 * x[n] = sqrt(2/N) sum e[k] X[k] * cos ((2n + 1)k * PI / 2N) 1438 * k = 0 1439 * use this DCT type. 1440 * This is the definition of the DCT used in Matlab. 1441 */ 1442 public static final int DCT_TYPE_ORTHONORMAL = 2; 1443 1444 /** The number of DCT types supported. */ 1445 public static final int DCT_TYPES = 3; 1446 1447 // Window types for generateWindow 1448 // In all of the formulas below, M = length of window - 1 1449 1450 /** To select the rectangular window, 1451 * <p> 1452 * w[n] = 1 for 0 ≤ n ≤ M 1453 * </p> 1454 * use this window type. 1455 */ 1456 public static final int WINDOW_TYPE_RECTANGULAR = 0; 1457 1458 /** To select the Bartlett (triangular) window, 1459 * <p> 1460 * w[n] = 2n/M for 0 ≤ n ≤ M/2 <br> 1461 * w[n] = 2 - 2n/M for M/2 < n ≤ M 1462 * </p> 1463 * use this window type. 1464 */ 1465 public static final int WINDOW_TYPE_BARTLETT = 1; 1466 1467 /** To select the Hanning window, 1468 * <p> 1469 * w[n] = 0.5 - 0.5 cos(2 * PI * n / M) <br> 1470 * for 0 ≤ n ≤ M 1471 * </p> 1472 * use this window type. 1473 */ 1474 public static final int WINDOW_TYPE_HANNING = 2; 1475 1476 /** To select the Hamming window, 1477 * <p> 1478 * w[n] = 0.54 - 0.46 cos(2 * PI * n / M) <br> 1479 * for 0 ≤ n ≤ M 1480 * </p> 1481 * use this window type. 1482 */ 1483 public static final int WINDOW_TYPE_HAMMING = 3; 1484 1485 /** To select the Blackman window, 1486 * <p> 1487 * w[n] = 0.42 - 0.5 cos(2 * PI * n /M) + <br> 1488 * 0.08 cos (4 * PI * n / M) <br> 1489 * for 0 ≤ n ≤ M 1490 * </p> 1491 * use this window type. 1492 */ 1493 public static final int WINDOW_TYPE_BLACKMAN = 4; 1494 1495 /** To select the 4-term Blackman-Harris window, 1496 * <p> 1497 * w[n] = 0.35875 - 0.48829 cos(2 * PI * n /M) + <br> 1498 * 0.14128 cos (4 * PI * n / M) - 0.01168 cos(6 * PI * n / M) <br> 1499 * for 0 ≤ n ≤ M 1500 * </p> 1501 * use this window type. 1502 */ 1503 public static final int WINDOW_TYPE_BLACKMAN_HARRIS = 5; 1504 1505 /** The number of window types that can be generated. */ 1506 public static final int WINDOW_TYPES = 6; 1507 1508 /////////////////////////////////////////////////////////////////// 1509 //// inner classes //// 1510 1511 /** This class generates samples of a Gaussian function with the 1512 * specified mean and standard deviation. 1513 * The function computed is : 1514 * <pre> 1515 * h(t) = (1/(sqrt(2 * PI) * stdDev) * 1516 * exp(-(t - mean)<sup>2</sup> / (2 * stdDev<sup>2</sup>)) 1517 * </pre> 1518 1519 */ 1520 public static class GaussianSampleGenerator 1521 implements DoubleUnaryOperation { 1522 /** Construct a GaussianSampleGenerator. 1523 * @param standardDeviation The standard deviation of the 1524 * Gaussian function. 1525 */ 1526 public GaussianSampleGenerator(double mean, double standardDeviation) { 1527 _mean = mean; 1528 _oneOverTwoVariance = 1.0 1529 / (2.0 * standardDeviation * standardDeviation); 1530 _factor = ONE_OVER_SQRT_TWO_PI / standardDeviation; 1531 } 1532 1533 /** Return a sample of the Gaussian function, sampled at the 1534 * specified time. 1535 */ 1536 @Override 1537 public final double operate(double time) { 1538 double shiftedTime = time - _mean; 1539 return _factor * Math 1540 .exp(-shiftedTime * shiftedTime * _oneOverTwoVariance); 1541 } 1542 1543 private final double _mean; 1544 1545 private final double _oneOverTwoVariance; 1546 1547 private final double _factor; 1548 1549 private static final double ONE_OVER_SQRT_TWO_PI = 1.0 1550 / Math.sqrt(2 * Math.PI); 1551 } 1552 1553 /** This class generates samples of a polynomial. 1554 * The function computed is: 1555 * <pre> 1556 * h(t) = a<sub>0</sub> + a<sub>1</sub>t + a<sub>2</sub>t<sup>2</sup> + ... 1557 * + a<sub>n-1</sub>t<sup>n-1</sup> 1558 * </pre> 1559 * or 1560 * <pre> 1561 * h(t) = a<sub>0</sub> + a<sub>1</sub>t<sup>-1</sup> 1562 * + a<sub>2</sub>t<sup>-2</sup> + ... 1563 * + a<sub>n-1</sub>t<sup>-(n-1)</sup> 1564 * </pre> 1565 * depending on the direction specified. 1566 */ 1567 public static class PolynomialSampleGenerator 1568 implements DoubleUnaryOperation { 1569 /** Construct a PolynomialSampleGenerator. The input argument is 1570 * an array of doubles 1571 * a[0] = a<sub>0</sub> .. a[n-1] = a<sub>n-1</sub> used to compute 1572 * the formula : 1573 * h(t) = a<sub>0</sub> + a<sub>1</sub>t + ... + a<sub>n-1</sub>t<sup>n-1</sup> 1574 * The array of doubles must be of length 1 or greater. 1575 * The array of doubles is copied, so the user is free to modify 1576 * it after construction. 1577 * The exponents on t in the above equation will all be negated if 1578 * the direction parameter is -1; otherwise the direction parameter 1579 * should be 1. 1580 * @param coefficients An array of double coefficients. 1581 * @param direction 1 for positive exponents, -1 for negative 1582 * exponents. 1583 */ 1584 public PolynomialSampleGenerator(double[] coefficients, int direction) { 1585 if (direction != 1 && direction != -1) { 1586 throw new IllegalArgumentException( 1587 "ptolemy.math.SignalProcessing.LineSampleGenerator: " 1588 + "direction must be either 1 or -1"); 1589 } 1590 1591 _coeffLength = coefficients.length; 1592 1593 // copy coefficient array 1594 _coefficients = DoubleArrayMath.resize(coefficients, _coeffLength); 1595 _direction = direction; 1596 } 1597 1598 /** Return a sample of the line, sampled at the specified time. 1599 * Note that at time = 0, with a negative direction, the sample 1600 * will be positive or negative infinity. 1601 */ 1602 @Override 1603 public final double operate(double time) { 1604 double sum = _coefficients[0]; 1605 double tn = time; 1606 1607 for (int i = 1; i < _coeffLength; i++) { 1608 if (_direction == 1) { 1609 sum += _coefficients[i] * tn; 1610 } else { 1611 sum += _coefficients[i] / tn; 1612 } 1613 1614 tn *= time; 1615 } 1616 1617 return sum; 1618 } 1619 1620 private final double[] _coefficients; 1621 1622 private final int _coeffLength; 1623 1624 private final int _direction; 1625 } 1626 1627 /** This class generates samples of a sawtooth wave with the specified 1628 * period and phase. The returned values range between -1.0 and 1.0. 1629 */ 1630 public static class SawtoothSampleGenerator 1631 implements DoubleUnaryOperation { 1632 /** Construct a SawtoothSampleGenerator with the given period and 1633 * phase. The phase is given as a fraction of a cycle, 1634 * typically ranging from 0.0 to 1.0. If the phase is 0.0 or 1.0, 1635 * the wave begins at zero with a rising slope. If it is 0.5, it 1636 * begins at the falling edge with value -1.0. 1637 * If it is 0.25, it begins at +0.5. 1638 */ 1639 public SawtoothSampleGenerator(double period, double phase) { 1640 _period = period; 1641 _phase = phase; 1642 } 1643 1644 /** Return a sample of the sawtooth wave, sampled at the 1645 * specified time. 1646 */ 1647 @Override 1648 public final double operate(double time) { 1649 return sawtooth(_period, _phase, time); 1650 } 1651 1652 private final double _period; 1653 1654 private final double _phase; 1655 } 1656 1657 /** This class generates samples of a sinusoidal wave. 1658 * The function computed is : 1659 * <pre> 1660 * h(t) = cos(frequency * t + phase) 1661 * </pre> 1662 * where the argument is taken to be in radians. 1663 * To use this class to generate a sine wave, simply 1664 * set the phase to -Math.PI*0.5 from the 1665 * phase, since sin(t) = cos(t - PI/2). 1666 */ 1667 public static class SinusoidSampleGenerator 1668 implements DoubleUnaryOperation { 1669 /** 1670 * Construct a SinusoidSampleGenerator. 1671 * @param frequency The frequency of the cosine wave, in radians per 1672 * unit time. 1673 * @param phase The phase shift, in radians. 1674 */ 1675 public SinusoidSampleGenerator(double frequency, double phase) { 1676 _frequency = frequency; 1677 _phase = phase; 1678 } 1679 1680 @Override 1681 public final double operate(double time) { 1682 return Math.cos(_frequency * time + _phase); 1683 } 1684 1685 private final double _frequency; 1686 1687 private final double _phase; 1688 } 1689 1690 /** This class generates samples of a raised cosine pulse, or if the 1691 * excess is zero, a modified sinc function. 1692 * <p> 1693 * The function that is computed is: 1694 * <p> 1695 * <pre> 1696 * sin(PI t/T) cos(excess PI t/T) 1697 * h(t) = ----------- * ----------------- 1698 * PI t/T 1-(2 excess t/T)<sup>2</sup> 1699 * </pre> 1700 * <p> 1701 * This is called a "raised cosine pulse" because in the frequency 1702 * domain its shape is that of a raised cosine. 1703 * <p> 1704 * For some applications, you may wish to apply a window function to this 1705 * impulse response, since it is rather abruptly terminated at the two \ 1706 * ends. 1707 * <p> 1708 * This implementation is ported from the Ptolemy 0.x implementation 1709 * by Joe Buck, Brian Evans, and Edward A. Lee. 1710 * Reference: <a href=http://www.amazon.com/exec/obidos/ASIN/0792393910/qid%3D910596335/002-4907626-8092437>E. A. Lee and D. G. Messerschmitt, 1711 * <i>Digital Communication, Second Edition</i>, 1712 * Kluwer Academic Publishers, Boston, 1994.</a> 1713 * 1714 */ 1715 public static class RaisedCosineSampleGenerator 1716 implements DoubleUnaryOperation { 1717 /** Construct a RaisedCosineSampleGenerator. 1718 * @param firstZeroCrossing The time of the first zero crossing, 1719 * after time zero. This would be the symbol interval in a 1720 * communications application of this pulse. 1721 * @param excess The excess bandwidth (in the range 0.0 to 1.0), also 1722 * called the rolloff factor. 1723 */ 1724 public RaisedCosineSampleGenerator(double firstZeroCrossing, 1725 double excess) { 1726 _oneOverFZC = 1.0 / firstZeroCrossing; 1727 _excess = excess; 1728 } 1729 1730 /** Return a sample of the raised cosine pulse, sampled at the 1731 * specified time. 1732 */ 1733 @Override 1734 public final double operate(double time) { 1735 if (time == 0.0) { 1736 return 1.0; 1737 } 1738 1739 double x = time * _oneOverFZC; 1740 double s = sinc(Math.PI * x); 1741 1742 if (_excess == 0.0) { 1743 return s; 1744 } 1745 1746 x *= _excess; 1747 1748 double denominator = 1.0 - 4.0 * x * x; 1749 1750 // If the denominator is close to zero, take it to be zero. 1751 if (close(denominator, 0.0)) { 1752 return s * ExtendedMath.PI_OVER_4; 1753 } 1754 1755 return s * Math.cos(Math.PI * x) / denominator; 1756 } 1757 1758 private final double _oneOverFZC; 1759 1760 private final double _excess; 1761 } 1762 1763 /** This class generates samples of a sinc wave with the specified 1764 * first zero crossing. 1765 */ 1766 public static class SincSampleGenerator implements DoubleUnaryOperation { 1767 @Override 1768 public final double operate(double time) { 1769 return sinc(time); 1770 } 1771 } 1772 1773 /** This class generates samples of a square-root raised cosine pulse. 1774 * The function computed is: 1775 * <p> 1776 * <pre> 1777 * 4 x(cos((1+x)PI t/T) + T sin((1-x)PI t/T)/(4x t/T)) 1778 * h(t) = --------------------------------------------------- 1779 * PI sqrt(T)(1-(4 x t/T)<sup>2</sup>) 1780 * </pre> 1781 * <p> 1782 * where <i>x</i> is the the excess bandwidth. 1783 * This pulse convolved with itself will, in principle, be equal 1784 * to a raised cosine pulse. However, because the pulse decays rather 1785 * slowly for low excess bandwidth, this ideal is not 1786 * closely approximated by short finite approximations of the pulse. 1787 * <p> 1788 * This implementation was ported from the Ptolemy 0.x implementation 1789 * by Joe Buck, Brian Evans, and Edward A. Lee. 1790 * Reference: E. A. Lee and D. G. Messerschmitt, 1791 * <i>Digital Communication, Second Edition</i>, 1792 * Kluwer Academic Publishers, Boston, 1994. 1793 * 1794 * The implementation was then further optimized to cache computations 1795 * that are independent of time. 1796 */ 1797 public static class SqrtRaisedCosineSampleGenerator 1798 implements DoubleUnaryOperation { 1799 /** Construct a SqrtRaisedCosineSampleGenerator. 1800 * @param firstZeroCrossing The time of the first zero crossing of 1801 * the corresponding raised cosine pulse. 1802 * @param excess The excess bandwidth of the corresponding raised 1803 * cosine pulse (also called the rolloff factor). 1804 */ 1805 public SqrtRaisedCosineSampleGenerator(double firstZeroCrossing, 1806 double excess) { 1807 _excess = excess; 1808 1809 _oneOverFZC = 1.0 / firstZeroCrossing; 1810 _sqrtFZC = Math.sqrt(firstZeroCrossing); 1811 _squareFZC = firstZeroCrossing * firstZeroCrossing; 1812 1813 _onePlus = (1.0 + _excess) * Math.PI * _oneOverFZC; 1814 _oneMinus = (1.0 - _excess) * Math.PI * _oneOverFZC; 1815 1816 _fourExcess = 4.0 * _excess; 1817 _eightExcessPI = 8.0 * _excess * Math.PI; 1818 _sixteenExcessSquared = _fourExcess * _fourExcess; 1819 1820 double fourExcessOverPI = _fourExcess / Math.PI; 1821 double oneOverSqrtFZC = 1.0 / _sqrtFZC; 1822 1823 _sampleAtZero = (fourExcessOverPI + 1.0 - _excess) * oneOverSqrtFZC; 1824 _fourExcessOverPISqrtFZC = fourExcessOverPI * oneOverSqrtFZC; 1825 1826 _fzcSqrtFZCOverEightExcessPI = firstZeroCrossing * _sqrtFZC 1827 / _eightExcessPI; 1828 _fzcOverFourExcess = firstZeroCrossing / _fourExcess; 1829 1830 _oneMinusFZCOverFourExcess = _oneMinus * _fzcOverFourExcess; 1831 } 1832 1833 /* Return a sample of the raised cosine pulse, sampled at the 1834 * specified time. 1835 * @param time The time at which to sample the pulse. 1836 * @return A double. 1837 */ 1838 @Override 1839 public final double operate(double time) { 1840 if (time == 0.0) { 1841 return _sampleAtZero; 1842 } 1843 1844 double x = time * _oneOverFZC; 1845 1846 if (_excess == 0.0) { 1847 return _sqrtFZC * Math.sin(Math.PI * x) / (Math.PI * time); 1848 } 1849 1850 double oneMinusTime = _oneMinus * time; 1851 double onePlusTime = _onePlus * time; 1852 1853 // Check to see whether we will get divide by zero. 1854 double denominator = time * time * _sixteenExcessSquared 1855 - _squareFZC; 1856 1857 if (close(denominator, 0.0)) { 1858 double oneOverTime = 1.0 / time; 1859 double oneOverTimeSquared = oneOverTime * oneOverTime; 1860 1861 return _fzcSqrtFZCOverEightExcessPI * oneOverTime 1862 * (_onePlus * Math.sin(onePlusTime) 1863 - _oneMinusFZCOverFourExcess * oneOverTime 1864 * Math.cos(oneMinusTime) 1865 + _fzcOverFourExcess * oneOverTimeSquared 1866 * Math.sin(oneMinusTime)); 1867 } 1868 1869 return _fourExcessOverPISqrtFZC 1870 * (Math.cos(onePlusTime) 1871 + Math.sin(oneMinusTime) / (x * _fourExcess)) 1872 / (1.0 - _sixteenExcessSquared * x * x); 1873 } 1874 1875 private final double _oneOverFZC; 1876 1877 private final double _sqrtFZC; 1878 1879 private final double _squareFZC; 1880 1881 private final double _onePlus; 1882 1883 private final double _oneMinus; 1884 1885 private final double _excess; 1886 1887 private final double _fourExcess; 1888 1889 private final double _eightExcessPI; 1890 1891 private final double _sixteenExcessSquared; 1892 1893 private final double _sampleAtZero; 1894 1895 private final double _fourExcessOverPISqrtFZC; 1896 1897 private final double _fzcSqrtFZCOverEightExcessPI; 1898 1899 private final double _fzcOverFourExcess; 1900 1901 private final double _oneMinusFZCOverFourExcess; 1902 } 1903 1904 /////////////////////////////////////////////////////////////////// 1905 //// private methods //// 1906 // Check that the order of a transform is between 0 and 31, inclusive. 1907 // Throw an exception otherwise. 1908 private static void _checkTransformOrder(int order) { 1909 if (order < 0) { 1910 throw new IllegalArgumentException( 1911 "ptolemy.math.SignalProcessing : order of transform " 1912 + "must be non-negative."); 1913 } else if (order > 31) { 1914 throw new IllegalArgumentException( 1915 "ptolemy.math.SignalProcessing : order of transform " 1916 + "must be less than 32."); 1917 } 1918 } 1919 1920 // Check the arguments for a transform on an array of doubles, using 1921 // _checkTransformInput() and _checkTransformOrder(). Return an 1922 // appropriately padded array on which to perform the transform. 1923 private static double[] _checkTransformArgs(double[] x, int order, 1924 boolean inverse) { 1925 _checkTransformOrder(order); 1926 1927 int size = 1 << order; 1928 1929 // Zero pad the array if necessary 1930 if (x.length < size) { 1931 x = inverse ? DoubleArrayMath.padMiddle(x, size) 1932 : DoubleArrayMath.resize(x, size); 1933 } 1934 1935 return x; 1936 } 1937 1938 // Check the arguments for a transform on an array of Complex's, using 1939 // _checkTransformInput() and _checkTransformOrder(). Return an 1940 // appropriately padded array on which to perform the transform. 1941 private static Complex[] _checkTransformArgs(Complex[] x, int order, 1942 boolean inverse) { 1943 _checkTransformOrder(order); 1944 1945 int size = 1 << order; 1946 1947 // Zero pad the array if necessary 1948 if (x.length < size) { 1949 x = inverse == _INVERSE_TRANSFORM 1950 ? ComplexArrayMath.padMiddle(x, size) 1951 : ComplexArrayMath.resize(x, size); 1952 } 1953 1954 return x; 1955 } 1956 1957 // Returns an array with half the size + 1 because of the symmetry 1958 // of the cosDFT function. 1959 private static double[] _cosDFT(double[] x, int size, int order) { 1960 switch (size) { 1961 // Base cases for lower orders 1962 case 0: 1963 return null; // should never be used 1964 1965 case 1: { 1966 double[] returnValue = new double[1]; 1967 returnValue[0] = x[0]; 1968 return returnValue; 1969 } 1970 1971 case 2: { 1972 double[] returnValue = new double[2]; 1973 returnValue[0] = x[0] + x[1]; 1974 returnValue[1] = x[0] - x[1]; 1975 return returnValue; 1976 } 1977 1978 // Optimized base case for higher orders 1979 case 4: { 1980 double[] returnValue = new double[3]; 1981 returnValue[0] = x[0] + x[1] + x[2] + x[3]; 1982 returnValue[1] = x[0] - x[2]; 1983 returnValue[2] = x[0] - x[1] + x[2] - x[3]; 1984 return returnValue; 1985 } 1986 } 1987 1988 int halfN = size >> 1; 1989 int quarterN = size >> 2; 1990 1991 double[] x1 = new double[halfN]; 1992 1993 for (int k = 0; k < halfN; k++) { 1994 x1[k] = x[k << 1]; 1995 } 1996 1997 double[] x2 = new double[quarterN]; 1998 1999 for (int k = 0; k < quarterN; k++) { 2000 int twoIp = (k << 1) + 1; 2001 x2[k] = x[twoIp] + x[size - twoIp]; 2002 } 2003 2004 double[] halfCosDFT = _cosDFT(x1, halfN, order - 1); 2005 double[] quarterDCT = _DCT(x2, quarterN, order - 2); 2006 2007 double[] returnValue = new double[halfN + 1]; 2008 2009 for (int k = 0; k < quarterN; k++) { 2010 returnValue[k] = halfCosDFT[k] + quarterDCT[k]; 2011 } 2012 2013 returnValue[quarterN] = halfCosDFT[quarterN]; 2014 2015 for (int k = quarterN + 1; k <= halfN; k++) { 2016 int idx = halfN - k; 2017 returnValue[k] = halfCosDFT[idx] - quarterDCT[idx]; 2018 } 2019 2020 return returnValue; 2021 } 2022 2023 // Returns an array with half the size because of the symmetry 2024 // of the sinDFT function. 2025 private static double[] _sinDFT(double[] x, int size, int order) { 2026 switch (size) { 2027 // Base cases for lower orders 2028 case 0: 2029 case 1: 2030 case 2: 2031 return null; // should never be used 2032 2033 // Optimized base case for higher orders 2034 case 4: { 2035 double[] returnValue = new double[2]; 2036 2037 // returnValue[0] = 0.0; // not necessary for Java, 2038 // also not read 2039 returnValue[1] = x[1] - x[3]; 2040 return returnValue; 2041 } 2042 } 2043 2044 int halfN = size >> 1; 2045 int quarterN = size >> 2; 2046 2047 double[] x1 = new double[halfN]; 2048 2049 for (int k = 0; k < halfN; k++) { 2050 x1[k] = x[k << 1]; 2051 } 2052 2053 double[] x3 = new double[quarterN]; 2054 2055 for (int k = 0; k < quarterN; k++) { 2056 int twoIp = (k << 1) + 1; 2057 x3[k] = (k & 1) == 1 ? x[size - twoIp] - x[twoIp] 2058 : x[twoIp] - x[size - twoIp]; 2059 } 2060 2061 double[] halfSinDFT = _sinDFT(x1, halfN, order - 1); 2062 double[] quarterDCT = _DCT(x3, quarterN, order - 2); 2063 2064 double[] returnValue = new double[halfN]; 2065 2066 // returnValue[0] = 0.0; // not necessary in Java 2067 for (int k = 1; k < quarterN; k++) { 2068 returnValue[k] = halfSinDFT[k] + quarterDCT[quarterN - k]; 2069 } 2070 2071 returnValue[quarterN] = quarterDCT[0]; 2072 2073 for (int k = quarterN + 1; k < halfN; k++) { 2074 returnValue[k] = quarterDCT[k - quarterN] - halfSinDFT[halfN - k]; 2075 } 2076 2077 return returnValue; 2078 } 2079 2080 private static double[] _DCT(double[] x, int size, int order) { 2081 double[] returnValue; 2082 2083 if (size == 1) { 2084 returnValue = new double[1]; 2085 returnValue[0] = x[0]; 2086 return returnValue; 2087 } 2088 2089 if (size == 2) { 2090 returnValue = new double[2]; 2091 returnValue[0] = x[0] + x[1]; 2092 returnValue[1] = ExtendedMath.ONE_OVER_SQRT_2 * (x[0] - x[1]); 2093 return returnValue; 2094 } 2095 2096 int halfN = size >> 1; 2097 2098 double[] x4 = new double[size]; 2099 2100 for (int n = 0; n < halfN; n++) { 2101 int twoN = n << 1; 2102 2103 // Do zero padding here 2104 if (twoN >= x.length) { 2105 x4[n] = 0.0; 2106 } else { 2107 x4[n] = x[twoN]; 2108 } 2109 2110 if (twoN + 1 >= x.length) { 2111 x4[size - n - 1] = 0.0; 2112 } else { 2113 x4[size - n - 1] = x[twoN + 1]; 2114 } 2115 } 2116 2117 double[] cosDFTarray = _cosDFT(x4, size, order); 2118 double[] sinDFTarray = _sinDFT(x4, size, order); 2119 2120 double[] p1tab = _P1Table[order]; 2121 double[] p2tab = _P2Table[order]; 2122 double[] ctab = _CTable[order]; 2123 2124 returnValue = new double[size]; 2125 2126 returnValue[0] = cosDFTarray[0]; 2127 2128 for (int k = 1; k < halfN; k++) { 2129 double m1 = (cosDFTarray[k] + sinDFTarray[k]) * ctab[k]; 2130 double m2 = sinDFTarray[k] * p1tab[k]; 2131 double m3 = cosDFTarray[k] * p2tab[k]; 2132 returnValue[k] = m1 - m2; 2133 returnValue[size - k] = m1 + m3; 2134 } 2135 2136 returnValue[halfN] = ExtendedMath.ONE_OVER_SQRT_2 * cosDFTarray[halfN]; 2137 2138 return returnValue; 2139 } 2140 2141 private synchronized static void _FFCTTableGen(int limit) { 2142 for (int i = _FFCTGenLimit; i <= limit; i++) { 2143 int N = 1 << i; // Watch out for this if i ever becomes > 31 2144 _P1Table[i] = new double[N]; 2145 _P2Table[i] = new double[N]; 2146 _CTable[i] = new double[N]; 2147 2148 double[] p1t = _P1Table[i]; 2149 double[] p2t = _P2Table[i]; 2150 double[] ct = _CTable[i]; 2151 2152 for (int k = 0; k < N; k++) { 2153 double arg = Math.PI * k / (2.0 * N); 2154 double c = Math.cos(arg); 2155 double s = Math.sin(arg); 2156 p1t[k] = c + s; 2157 p2t[k] = s - c; 2158 ct[k] = c; 2159 } 2160 } 2161 2162 _FFCTGenLimit = Math.max(_FFCTGenLimit, limit); 2163 } 2164 2165 /////////////////////////////////////////////////////////////////// 2166 //// private members //// 2167 // Tables needed for the FFCT algorithm. We assume no one will attempt 2168 // to do a transform of size greater than 2^31. 2169 private static final double[][] _P1Table = new double[32][]; 2170 2171 private static final double[][] _P2Table = new double[32][]; 2172 2173 private static final double[][] _CTable = new double[32][]; 2174 2175 private static int _FFCTGenLimit = 0; 2176 2177 // Table of scaleFactors for the IDCT. 2178 private static final Complex[][][] _IDCTfactors = new Complex[DCT_TYPES][32][]; 2179 2180 // Various constants 2181 private static final double _LOG10SCALE = 1.0 / Math.log(10.0); 2182 2183 private static final double _LOG2SCALE = 1.0 / Math.log(2.0); 2184 2185 // Indicates a forward/inverse transform for checking arguments 2186 private static final boolean _FORWARD_TRANSFORM = false; 2187 2188 private static final boolean _INVERSE_TRANSFORM = true; 2189}