Example #1
0
int PSDCalculator::calculatePowerSpectrum(
  double *input, int inputLen, 
  double *output, int outputLen, 
  bool removeMean, bool interpolateHoles,
  bool average, int averageLen, 
  bool apodize, ApodizeFunction apodizeFxn, double gaussianSigma,
  PSDType outputType, double inputSamplingFreq) {

  if (outputLen != calculateOutputVectorLength(inputLen, average, averageLen)) {
    KstDebug::self()->log(QObject::tr("in PSDCalculator::calculatePowerSpectrum: received output array with wrong length."), KstDebug::Error);

    return -1;
  }

  if (outputLen != _prevOutputLen) {
    delete[] _a;
    delete[] _w;

    _awLen = outputLen*2;
    _prevOutputLen = outputLen;

    _a = new double[_awLen];
    _w = new double[_awLen];

    updateWindowFxn(apodizeFxn, gaussianSigma);
  }

  if ( (_prevApodizeFxn != apodizeFxn) || (_prevGaussianSigma != gaussianSigma) ) {
    updateWindowFxn(apodizeFxn, gaussianSigma);
  }

  int currentCopyLen, nsamples = 0;
  int i_samp, i_subset, ioffset;

  memset(output, 0, sizeof(double) * outputLen);

  bool done = false;
  for (i_subset = 0; !done; i_subset++) {
    //
    // overlapping average => i_subset*outputLen...
    //

    ioffset = i_subset * outputLen;

    //
    // only zero pad if we really have to.  
    //  It is better to adjust the last chunk's overlap...
    //

    if (ioffset + _awLen*5/4 < inputLen) {
      currentCopyLen = _awLen; // will copy a complete window.
    } else if (_awLen<inputLen) {  // count the last one from the end.
      ioffset = inputLen-_awLen - 1;
      currentCopyLen = _awLen; // will copy a complete window.
      done = true;
    } else {
      currentCopyLen = inputLen - ioffset; // will copy a partial window.
      memset(&_a[currentCopyLen], 0, sizeof(double)*(_awLen - currentCopyLen)); // zero the leftovers.
      done = true;
    }

    double mean = 0.0;

    if (removeMean) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        mean += input[i_samp + ioffset];
      }
      mean /= (double)currentCopyLen;
    }

    //
    // apply the PSD options (removeMean, apodize, etc.)
    // separate cases for speed- although this shouldn't really matter - 
    //  the rdft should be the most time consuming step by far for any large data set...
    //

    if (removeMean && apodize && interpolateHoles) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = (kstInterpolateNoHoles(input, inputLen, i_samp + ioffset, inputLen) - mean)*_w[i_samp];
      }
    } else if (removeMean && apodize) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = (input[i_samp + ioffset] - mean)*_w[i_samp];
      }
    } else if (removeMean && interpolateHoles) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = kstInterpolateNoHoles(input, inputLen, i_samp + ioffset, inputLen) - mean;
      }
    } else if (apodize && interpolateHoles) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = kstInterpolateNoHoles(input, inputLen, i_samp + ioffset, inputLen)*_w[i_samp];
      }
    } else if (removeMean) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = input[i_samp + ioffset] - mean;
      }
    } else if (apodize) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = input[i_samp + ioffset]*_w[i_samp];
      }
    } else if (interpolateHoles) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = kstInterpolateNoHoles(input, inputLen, i_samp + ioffset, inputLen);
      }
    } else {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = input[i_samp + ioffset];
      }
    }

    nsamples += currentCopyLen;

    //
    // real discrete fourier transorm on _a.
    //

    rdft(_awLen, 1, _a); 

    output[0] += _a[0] * _a[0];
    output[outputLen-1] += _a[1] * _a[1];
    for (i_samp = 1; i_samp < outputLen - 1; i_samp++) {
      output[i_samp] += cabs2(_a[i_samp * 2], _a[i_samp * 2 + 1]);
    }
  }

  double frequencyStep = 2.0*(double)inputSamplingFreq/(double)nsamples;
  double norm = 2.0/(double)nsamples*2.0/(double)nsamples;

  switch (outputType) {
    default:
    case PSDAmplitudeSpectralDensity: // amplitude spectral density (default) [V/Hz^1/2]
      norm /= frequencyStep;
      for (i_samp = 0; i_samp < outputLen; i_samp++) {
        output[i_samp] = sqrt(output[i_samp]*norm);
      }
      break;

    case PSDPowerSpectralDensity: // power spectral density [V^2/Hz]
      norm /= frequencyStep;
      for (i_samp = 0; i_samp < outputLen; i_samp++) {
        output[i_samp] *= norm;
      }
      break;

    case PSDAmplitudeSpectrum: // amplitude spectrum [V]
      for (i_samp = 0; i_samp < outputLen; i_samp++) {
        output[i_samp] = sqrt(output[i_samp]*norm);
      }
      break;

    case PSDPowerSpectrum: // power spectrum [V^2]
      for (i_samp = 0; i_samp < outputLen; i_samp++) {
        output[i_samp] *= norm;
      }
      break;
  }

  return 0;
}
int PSDCalculator::calculatePowerSpectrum(
  double *input, int inputLen, 
  double *output, int outputLen, 
  bool removeMean, bool interpolateHoles,
  bool average, int averageLen, 
  bool apodize, ApodizeFunction apodizeFxn, double gaussianSigma,
  PSDType outputType, double inputSamplingFreq) {

  if (outputLen != calculateOutputVectorLength(inputLen, average, averageLen)) {
    Kst::Debug::self()->log(i18n("in PSDCalculator::calculatePowerSpectrum: received output array with wrong length."), Kst::Debug::Error);
    return -1;
  }

  if (outputLen != _prevOutputLen) {
    delete[] _a;
    delete[] _w;

    _awLen = outputLen*2;
    _prevOutputLen = outputLen;

    _a = new double[_awLen];
    _w = new double[_awLen];

    updateWindowFxn(apodizeFxn, gaussianSigma);
  }

  if ( (_prevApodizeFxn != apodizeFxn) || (_prevGaussianSigma != gaussianSigma) ) {
    updateWindowFxn(apodizeFxn, gaussianSigma);
  }

  int currentCopyLen, nsamples = 0;
  int i_samp, i_subset, ioffset;

  memset(output, 0, sizeof(double)*outputLen); // initialize output.

  // Mingw build could be 10 times slower (Gaussian apod, mostly 0 then?)
  //MeasureTime time_in_rfdt("rdft()");

  bool done = false;
  for (i_subset = 0; !done; i_subset++) {
    ioffset = i_subset*outputLen; //overlapping average => i_subset*outputLen

    // only zero pad if we really have to.  It is better to adjust the last chunk's
    // overlap.
    if (ioffset + _awLen*5/4 < inputLen) {
      currentCopyLen = _awLen; //will copy a complete window.
    } else if (_awLen<inputLen) {  // count the last one from the end.
      ioffset = inputLen-_awLen - 1;
      currentCopyLen = _awLen; //will copy a complete window.
      done = true;
    } else {
      currentCopyLen = inputLen - ioffset; //will copy a partial window.
      memset(&_a[currentCopyLen], 0, sizeof(double)*(_awLen - currentCopyLen)); //zero the leftovers.
      done = true;
    }

    double mean = 0.0;

    if (removeMean) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        mean += input[i_samp + ioffset];
      }
      mean /= (double)currentCopyLen;
    }

    // apply the PSD options (removeMean, apodize, etc.)
    // separate cases for speed- although this shouldn't really matter- the rdft should be the most time consuming step by far for any large data set.
    if (removeMean && apodize && interpolateHoles) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = (Kst::kstInterpolateNoHoles(input, inputLen, i_samp + ioffset, inputLen) - mean)*_w[i_samp];
      }
    } else if (removeMean && apodize) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = (input[i_samp + ioffset] - mean)*_w[i_samp];
      }
    } else if (removeMean && interpolateHoles) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = Kst::kstInterpolateNoHoles(input, inputLen, i_samp + ioffset, inputLen) - mean;
      }
    } else if (apodize && interpolateHoles) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = Kst::kstInterpolateNoHoles(input, inputLen, i_samp + ioffset, inputLen)*_w[i_samp];
      }
    } else if (removeMean) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = input[i_samp + ioffset] - mean;
      }
    } else if (apodize) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = input[i_samp + ioffset]*_w[i_samp];
      }
    } else if (interpolateHoles) {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = Kst::kstInterpolateNoHoles(input, inputLen, i_samp + ioffset, inputLen);
      }
    } else {
      for (i_samp = 0; i_samp < currentCopyLen; i_samp++) {
        _a[i_samp] = input[i_samp + ioffset];
      }
    }

    nsamples += currentCopyLen;

#if !defined(__QNX__)
    rdft(_awLen, 1, _a); //real discrete fourier transorm on _a.
#else
    Q_ASSERT(0); // there is a linking problem when not compling with pch. . .
#endif

    output[0] += _a[0] * _a[0];
    output[outputLen-1] += _a[1] * _a[1];
    for (i_samp = 1; i_samp < outputLen - 1; i_samp++) {
      output[i_samp] += cabs2(_a[i_samp * 2], _a[i_samp * 2 + 1]);
    }
  }

  // FIXME: NORMALIZATION. 
  /* This normalization doesn't give the same results as the original KstPSD.

  double frequencyStep = .5*(double)inputSamplingFreq/(double)(outputLen-1);

  //normalization factors which were left out earlier for speed. 
  //    - 2.0 for the negative frequencies which were neglected by the rdft //FIXME: double check.
  //    - /(_awLen*_awLen) for the constant Wss from numerical recipes in C. (ensure that the window function agrees with this.)
  //    - /i_subset to average the powers in all the subsets.
  double norm = 2.0/(double)_awLen/(double)_awLen/(double)i_subset;
  */

  // original normalization
  double frequencyStep = 2.0*(double)inputSamplingFreq/(double)nsamples; //OLD value for frequencyStep.
  double norm = 2.0/(double)nsamples*2.0/(double)nsamples; //OLD value for norm.

  switch (outputType) {
  default:
    case PSDAmplitudeSpectralDensity: // amplitude spectral density (default) [V/Hz^1/2]
      norm /= frequencyStep;
      for (i_samp = 0; i_samp < outputLen; i_samp++) {
        output[i_samp] = sqrt(output[i_samp]*norm);
      }
    break;

    case PSDPowerSpectralDensity: // power spectral density [V^2/Hz]
      norm /= frequencyStep;
      for (i_samp = 0; i_samp < outputLen; i_samp++) {
        output[i_samp] *= norm;
      }
    break;

    case PSDAmplitudeSpectrum: // amplitude spectrum [V]
      for (i_samp = 0; i_samp < outputLen; i_samp++) {
        output[i_samp] = sqrt(output[i_samp]*norm);
      }
    break;

    case PSDPowerSpectrum: // power spectrum [V^2]
      for (i_samp = 0; i_samp < outputLen; i_samp++) {
        output[i_samp] *= norm;
      }
    break;
  }

  return 0;
}