using System;
using System.Collections.Generic;
using System.Drawing;
#if !NETSTANDARD2_0
using System.Drawing.Drawing2D;
#endif
using System.Linq;
using System.Text;
namespace HslCommunication.Algorithms.Fourier
{
///
/// 离散傅氏变换的快速算法,处理的信号,适合单周期信号数为2的N次方个,支持变换及逆变换
///
public class FFTHelper
{
///
///
///
///
///
///
private static void bitrp( double[] xreal, double[] ximag, int n )
{
// 位反转置换 Bit-reversal Permutation
int i, j, a, b, p;
for (i = 1, p = 0; i < n; i *= 2)
{
p++;
}
for (i = 0; i < n; i++)
{
a = i;
b = 0;
for (j = 0; j < p; j++)
{
b = b * 2 + a % 2;
a = a / 2;
}
if (b > i)
{
double t = xreal[i];
xreal[i] = xreal[b];
xreal[b] = t;
t = ximag[i];
ximag[i] = ximag[b];
ximag[b] = t;
}
}
}
///
/// 快速傅立叶变换
///
/// 实数部分
/// 变换后的数组值
public static double[] FFT( double[] xreal )
{
return FFT( xreal, new double[xreal.Length] );
}
#if !NETSTANDARD2_0
///
/// 获取FFT变换后的显示图形,需要指定图形的相关参数
///
/// 实数部分的值
/// 图形的宽度
/// 图形的高度
/// 线条颜色
/// 等待呈现的图形
///
/// .net standrard2.0 下不支持。
///
public static Bitmap GetFFTImage( double[] xreal,int width,int heigh ,Color lineColor)
{
double[] ximag = new double[xreal.Length]; // 构造虚对象
double[] array = FFT( xreal, ximag ); // 傅立叶变换
Bitmap bitmap = new Bitmap( width, heigh ); // 构造图形
Graphics g = Graphics.FromImage( bitmap );
g.SmoothingMode = SmoothingMode.HighQuality;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.Clear( Color.White );
Pen pen_Line = new Pen( Color.DimGray, 1 ); // 定义画笔资源
Pen pen_Dash = new Pen( Color.LightGray, 1 );
Pen pen_Fourier = new Pen( lineColor, 1 );
pen_Dash.DashPattern = new float[2] { 5, 5 };
pen_Dash.DashStyle = DashStyle.Custom;
Font Font_Normal = SystemFonts.DefaultFont; // 定义字体资源
StringFormat sf_right = new StringFormat( );
sf_right.Alignment = StringAlignment.Far;
sf_right.LineAlignment = StringAlignment.Center;
StringFormat sf_center = new StringFormat( );
sf_center.LineAlignment = StringAlignment.Center;
sf_center.Alignment = StringAlignment.Center;
int padding_top = 20;
int padding_left = 49;
int padding_right = 49;
int padding_down = 30;
int sections = 9;
// g.DrawLine( pen_Line, new Point( padding_left, padding_top ), new Point( padding_left, heigh - padding_down ) );
float paint_height = heigh - padding_top - padding_down;
float paint_width = width - padding_left - padding_right;
if (array.Length > 1)
{
double Max = array.Max( );
double Min = array.Min( );
Max = Max - Min > 1 ? Max : Min + 1;
double Length = Max - Min;
//提取峰值
List Peaks = new List( );
if (array.Length >= 2)
{
if (array[0] > array[1])
{
Peaks.Add( 0 );
}
for (int i = 1; i < array.Length - 2; i++)
{
if (array[i - 1] < array[i] && array[i] > array[i + 1])
{
Peaks.Add( i );
}
}
if (array[array.Length - 1] > array[array.Length - 2])
{
Peaks.Add( array.Length - 1 );
}
}
//高400
for (int i = 0; i < sections; i++)
{
RectangleF rec = new RectangleF( -10f, (float)i / (sections - 1) * paint_height, padding_left + 8f, 20f );
double n = (sections - 1 - i) * Length / (sections - 1) + Min;
g.DrawString( n.ToString( "F1" ), Font_Normal, Brushes.Black, rec, sf_right );
g.DrawLine(
pen_Dash, padding_left - 3, paint_height * i / (sections - 1) + padding_top,
width - padding_right, paint_height * i / (sections - 1) + padding_top );
}
float intervalX = paint_width / array.Length; // 横向间隔
for (int i = 0; i < Peaks.Count; i++)
{
if (array[(int)Peaks[i]] * 200 / Max > 1)
{
g.DrawLine( pen_Dash, Peaks[i] * intervalX + padding_left + 1, padding_top, Peaks[i] * intervalX + padding_left + 1, heigh - padding_down );
RectangleF rec = new RectangleF( Peaks[i] * intervalX + padding_left + 1 - 40, heigh - padding_down + 1, 80f, 20f );
g.DrawString( Peaks[i].ToString( ), Font_Normal, Brushes.DeepPink, rec, sf_center );
}
}
for (int i = 0; i < array.Length; i++)
{
PointF point = new PointF( );
point.X = i * intervalX + padding_left + 1;
point.Y = (float)(paint_height - (array[i] - Min) * paint_height / Length + padding_top);
PointF point2 = new PointF( );
point2.X = i * intervalX + padding_left + 1;
point2.Y = (float)(paint_height - (Min - Min) * paint_height / Length + padding_top);
g.DrawLine( Pens.Tomato, point, point2 );
}
}
else
{
double Max = 100;
double Min = 0;
double Length = Max - Min;
//高400
for (int i = 0; i < sections; i++)
{
RectangleF rec = new RectangleF( -10f, (float)i / (sections - 1) * paint_height, padding_left + 8f, 20f );
double n = (sections - 1 - i) * Length / (sections - 1) + Min;
g.DrawString( n.ToString( "F1" ), Font_Normal, Brushes.Black, rec, sf_right );
g.DrawLine(
pen_Dash, padding_left - 3, paint_height * i / (sections - 1) + padding_top,
width - padding_right, paint_height * i / (sections - 1) + padding_top );
}
}
pen_Dash.Dispose( );
pen_Line.Dispose( );
pen_Fourier.Dispose( );
Font_Normal.Dispose( );
sf_right.Dispose( );
sf_center.Dispose( );
g.Dispose( );
return bitmap;
}
#endif
///
/// 快速傅立叶变换
///
/// 实数部分,数组长度最好为2的n次方
/// 虚数部分,数组长度最好为2的n次方
/// 变换后的数组值
public static double[] FFT( double[] xreal, double[] ximag )
{
//n值为2的N次方
int n = 2;
while (n <= xreal.Length)
{
n *= 2;
}
n /= 2;
// 快速傅立叶变换,将复数 x 变换后仍保存在 x 中,xreal, ximag 分别是 x 的实部和虚部
double[] wreal = new double[n / 2];
double[] wimag = new double[n / 2];
double treal, timag, ureal, uimag, arg;
int m, k, j, t, index1, index2;
bitrp( xreal, ximag, n );
// 计算 1 的前 n / 2 个 n 次方根的共轭复数 W'j = wreal [j] + i * wimag [j] , j = 0, 1, ... , n / 2 - 1
arg = (-2 * Math.PI / n);
treal = Math.Cos( arg );
timag = Math.Sin( arg );
wreal[0] = 1.0f;
wimag[0] = 0.0f;
for (j = 1; j < n / 2; j++)
{
wreal[j] = wreal[j - 1] * treal - wimag[j - 1] * timag;
wimag[j] = wreal[j - 1] * timag + wimag[j - 1] * treal;
}
for (m = 2; m <= n; m *= 2)
{
for (k = 0; k < n; k += m)
{
for (j = 0; j < m / 2; j++)
{
index1 = k + j;
index2 = index1 + m / 2;
t = n * j / m; // 旋转因子 w 的实部在 wreal [] 中的下标为 t
treal = wreal[t] * xreal[index2] - wimag[t] * ximag[index2];
timag = wreal[t] * ximag[index2] + wimag[t] * xreal[index2];
ureal = xreal[index1];
uimag = ximag[index1];
xreal[index1] = ureal + treal;
ximag[index1] = uimag + timag;
xreal[index2] = ureal - treal;
ximag[index2] = uimag - timag;
}
}
}
double[] result = new double[n];
for (int i = 0; i < result.Length; i++)
{
result[i] = Math.Sqrt( Math.Pow( xreal[i], 2 ) + Math.Pow( ximag[i], 2 ) );
}
return result;
}
///
/// 快速傅立叶变换的逆变换
///
/// 实数部分,数组长度最好为2的n次方
/// 虚数部分,数组长度最好为2的n次方
/// 2的多少次方
public static int IFFT( double[] xreal, double[] ximag )
{
//n值为2的N次方
int n = 2;
while (n <= xreal.Length)
{
n *= 2;
}
n /= 2;
// 快速傅立叶逆变换
double[] wreal = new double[n / 2];
double[] wimag = new double[n / 2];
double treal, timag, ureal, uimag, arg;
int m, k, j, t, index1, index2;
bitrp( xreal, ximag, n );
// 计算 1 的前 n / 2 个 n 次方根 Wj = wreal [j] + i * wimag [j] , j = 0, 1, ... , n / 2 - 1
arg = (2 * Math.PI / n);
treal = (Math.Cos( arg ));
timag = (Math.Sin( arg ));
wreal[0] = 1.0f;
wimag[0] = 0.0f;
for (j = 1; j < n / 2; j++)
{
wreal[j] = wreal[j - 1] * treal - wimag[j - 1] * timag;
wimag[j] = wreal[j - 1] * timag + wimag[j - 1] * treal;
}
for (m = 2; m <= n; m *= 2)
{
for (k = 0; k < n; k += m)
{
for (j = 0; j < m / 2; j++)
{
index1 = k + j;
index2 = index1 + m / 2;
t = n * j / m; // 旋转因子 w 的实部在 wreal [] 中的下标为 t
treal = wreal[t] * xreal[index2] - wimag[t] * ximag[index2];
timag = wreal[t] * ximag[index2] + wimag[t] * xreal[index2];
ureal = xreal[index1];
uimag = ximag[index1];
xreal[index1] = ureal + treal;
ximag[index1] = uimag + timag;
xreal[index2] = ureal - treal;
ximag[index2] = uimag - timag;
}
}
}
for (j = 0; j < n; j++)
{
xreal[j] /= n;
ximag[j] /= n;
}
return n;
}
}
}