在C#中使用FFTW来计算HilbertTransform

时间:2016-06-23 09:59:48

标签: c# matlab

我想在C#中实现希尔伯特变换。从this文章中我看到最快的FFT开源实现似乎是FFTW,所以我下载了这个例子并用它来学习如何使用fftw包装器来实现C#。

我有一个200,000点的电流信号,我用它进行测试。通过fft获得希尔伯特变换相对简单:

  1. 计算fft。
  2. 乘以除了DC和奈奎斯特分量之外的所有正频率(0和n / 2 + 1,如果样本大小均匀)。
  3. 将所有负频率([n / 2 + 1,n])乘以0。
  4. 计算逆fft。
  5. 到目前为止,我已经完成了所有这些工作。唯一的问题是逆fft。我用fftw得不到与matlab相同的结果。

    我的代码

        RealArray _input;
        ComplexArray _fft;
    
        void ComputeFFT()
        {
            _fft = new ComplexArray(_length / 2 + 1);
            _input.Set(Data);
            _plan = Plan.Create1(_length, _input, _fft, Options.Estimate);
            _plan.Execute();
        }
    

    到目前为止,我只有一个只有正频率的fft。因此,我不需要将负频率乘以零:它们甚至不存在。使用以下代码,我可以恢复原始信号:

        double[] ComputeIFFT(ComplexArray input)
        {
            double[] temp = new double[_length];
            RealArray output = new RealArray(_length);
    
            _plan = Plan.Create1(_length, input, output, Options.Estimate);
            _plan.Execute();
            temp = output.ToArray();
    
            for (int i = 0; i < _length; ++i)
            {
                temp[i] /= _length;
            }
    
            return temp;
        }
    

    当我尝试从信号中获得复数逆时,问题出现了。

        void ComputeHilbert()
        {
            double[] fft = FFT.ToArray();
            double[] h = new double[_length / 2 + 1];
            double[] temp = new double[_length * 2];
    
            bool fftLengthIsOdd = (_length | 1) == 1;
    
            h[0] = 1;
    
            for (int i = 1; i < _length / 2; i++) h[i] = 2;
    
            if (!fftLengthIsOdd) h[(_length / 2)] = 1;
    
            for (int i = 0; i <= _length / 2; i++)
            {
                temp[2 * i] = fft[2*i] * h[i];
                temp[2 * i + 1] = fft[2*i + 1] * h[i];
            }
    
            ComplexArray _tempHilbert = new ComplexArray(_length);
    
            _tempHilbert.Set(temp);
    
            _hilbert = ComputeIFFT(_tempHilbert);
            _hilbertComputed = true;
        }
    

    重要的是要注意,当我在ToArray()对象上应用ComplexArray方法时,我得到一个double[]的结果,其长度是原始对象的两倍数组,实部和虚部连续。对于包含&#34; 3 + 1i&#34;的ComplexArray对象,我会得到带有[3,1]的双向量。

    所以,在这一刻,我所拥有的是:

    [DC Frequency, 2*positive frequencies, Nyquist Frequency, zeros] 如果我将这些数据导出到Matlab并计算IFFT,我会得到与其hilbert(信号)相同的结果。

    但是,如果我尝试应用fftw提供的IFFT,我会从Nyquist Frequency到最终得到奇怪的值(也就是说,fftw的零点混乱)。

    这是我用来执行此操作的ifft:

    double[] ComputeIFFT(ComplexArray input)
    {
        double[] temp;
        ComplexArray output = new ComplexArray(_length);
    
        _plan = Plan.Create1(_length, input, output, Direction.Backward, Options.Estimate);
        _plan.Execute();
        temp = output.ToArray();
    
        for (int i = 0; i < _length; ++i)
        {
            temp[i] /= _length;
        }
    
        return temp;
    }
    

    所以,总而言之,我的问题就是我用来计算ifft的方式。它似乎与零一起运行良好。或者也许Matlab能够理解它必须应用一些不同的方法,我应该手动完成,但我不知道如何。

    非常感谢您的帮助,非常感谢!

1 个答案:

答案 0 :(得分:0)

所以问题是ComputeIFFT功能。在for循环中,我正在做我&lt; _length,但temp数组的长度为2 * _length,因为它包含实数和虚数值。

这就是为什么我只有一半的价值正确。

正确的代码是:

double[] ComputeIFFT(ComplexArray input)
{
    double[] temp;
    ComplexArray output = new ComplexArray(_length);

    _plan = Plan.Create1(_length, input, output, Direction.Backward, Options.Estimate);
    _plan.Execute();
    temp = output.ToArray();

    for (int i = 0; i < temp.Length; ++i)
    {
        temp[i] /= _length;
    }

    return temp;
}

我希望这对于试图在C#中通过FFTW实现希尔伯特变换的人来说非常有用。