最新写一个信号处理的项目,需要使用到各种变换,如傅里叶变换(FFT)、加窗、希尔伯特变换(hilbert)等,其中傅里叶变换使用MathNet.Numerics类中的 Fourier.Forward完成。但是希尔伯特变换hilbert却没有现成的方法,网上找了很多,有说能实现的,但测试结果与matlab的完全不一致,于是产看matlab中的hilbert函数,用C#完全复刻了一个,测试结果与matlab中的一模一样。
计算思路:
(1)输入为一段信号数组x。
(2)对数组进行傅里叶变换(FFT)用Fourier.Forward完成。结果为复数数组complex。
(3)构造一个数组h,长度与x相同。这个是重点,这个数组如何构造看代码,里面有注释,至于这个数组h为啥要这样设计,是数学的问题,我也不懂,matlab中的hilbert源码里面就是这样构建的。
(4)傅里叶变换结果数组complex,乘以数组h。结果也是一个复数数组。这个数组怎么相乘?就是complex数组中的每个元素都乘以数组h中相对应位置的元素。这里又有个问题,复数如何乘以实数?其实也简单,就是复数中的实部和虚部都相乘即可,即a*(b+ci)=a*b+a*ci。
(5)对(4)中的结果复数数组,进行傅里叶逆变换,得到的复数数组即为希尔伯特变换(hilbert函数)的结果。
(6)根据需要对这个希尔伯特变换(hilbert函数)结果复数数组进行其他处理。
这个希尔伯特变换提供一个思路,之后的其他变换,加窗,或者滤波,就是先构造一个数组h,再乘以傅里叶变换结果数组了。思路很重要。
代码如下,这几天看matlab代码看得头疼,给我点鼓励吧。
using MathNet.Numerics;
using MathNet.Numerics.IntegralTransforms;
using System;
public static Complex32[] hilbert(double[] x)
{
int L = x.Length;//信号数组长度
Complex32[] complex = new MathNet.Numerics.Complex32[L];//将实数信号序列构造复数序列,实数的就是虚部为0的复数。
for (int i = 0; i < L; i++)
{
complex[i] = new MathNet.Numerics.Complex32((float)x[i], 0);
}
Fourier.Forward(complex, FourierOptions.Matlab); //傅里叶变换,结果与matalb的一致,用到这里输出这个complex就这完成傅里叶变换,complex[i].Real为实部, complex[i].Imaginary为虚部
//构造序列h
float[] h = new float[L];
for (int i=0;i<h.Length;i++)//置空
{
h[i] = 0;
}
//h是否被2整除
if (L % 2 == 0)//是
{
h[0] = 1; //第一个为1
h[L / 2] = 1;//中间后一个位置为1
for (int i = 1; i <L / 2; i++)//第二个到中间位置前一个元素设置为 2
{
h[i] = 2;
}
}
else//否
{
h[0] = 1;//第一个为1
for (int i=1;i<(L+1)/2;i++)//第二个到中间位置的元素设置为 2
{
h[i] = 2;
}
}
//对傅里叶变换结果乘以序列h,乘法运算,复数实部和虚部都要乘乘数
for (int i = 0; i < complex.Length; i++)
{
float rel = complex[i].Real *h[i];//实部乘以序列h
float img = complex[i].Imaginary * h[i];//虚部乘以序列h,
complex[i] = new MathNet.Numerics.Complex32(rel, img);//相乘后的结果
}
Fourier.Inverse(complex, FourierOptions.Matlab);//逆傅里叶变换,完成希尔伯特变换,返回这个complex则完成希尔伯特变换,及matlab中的hilbert函数
return complex;
//Complex32是表示一个复数,例有复数2+3!,建立复数表示为 Complex32 complex=new MathNet.Numerics.Complex32(2, 3); 从这个复数中取实部 float rel = complex.Real;复数中取虚部float img = complex[i].Imaginary
}
以下是测试代码。
using MathNet.Numerics;
using System;
namespace WpfApp2
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : System.Windows.Window
{
public MainWindow()
{
InitializeComponent();
double[] x = { 1, 2, 3, 4, 5, 6, 7, 8 };
Complex32[] complex = MathUtils.hilbert(x);
for(int i = 0; i < complex.Length; i++)
{
Console.WriteLine(complex[i]);
}
}
}
}
matlab中是这样的
x=[1,2,3,4,5,6,7,8]
y=hilbert(x)
结果对比如图,结果完全一样