信号分析~模拟 生成信号并进行FFT分析
模拟生成信号数据,并进行FFT分析,流程如下:
在实际的代码执行中,我们必须先加窗,再做 FFT 计算。
🔍 现有流程的小修正与遗漏补充
我们先帮你把现有的流程理得更精准(顺便纠正一下步骤3、4的逻辑):
- 获取界面输入:读取信号频率、幅值、采样率、FFT窗口长度 N 等。
- 生成信号数据:生成连续的正弦波并塞入滑动缓冲区(
_sampleBuffer)。 - 截取信号(漏掉的物理动作):从缓冲区末尾往前截取最近的 N 个点(比如 4096 个点)作为当前计算组。
- 加汉宁窗处理(修正顺序):对这 N 个点逐个乘以汉宁窗系数,并将结果作为实部、虚部置为 0,构建
Complex(复数)数组。 - 执行 MathNet FFT:将加窗后的复数数组送入
Fourier.Forward。 - 单边谱幅值恢复:对 FFT 出来的复数取模值(
Magnitude),执行除以 N、乘以 2(单边谱恢复)、再乘以 2(汉宁窗能量恢复) 的操作。 - 界面绘制:计算 X 轴频率步长
xStep,绑定数据给 LightningChart 渲染。
关于你提到的步骤 3:其实在上一轮修改后,我们在
FillSpectrumChart内部已经彻底架空了 LightningChart 的_spectrumCalculator(步骤3里的旧操作已经没用了)。我们是直接去缓冲区拿的原始波形。
💡 纯粹使用 MathNet.Numerics 能简化流程吗?
答案是:可以,而且可以让代码变得极其优雅、高逼格!
目前我们的加窗(for 循环计算 cos)、幅值恢复都是自己手写的循环。既然要全套使用 MathNet,我们就可以利用它自带的 Window(窗函数组件)。
MathNet 内部自带了非常高效的 Window.Hann(N) 或者是 Window.HannPeriodic(N) 方法,可以直接一行代码生成整个窗系数数组。
优化后的纯 MathNet 极简核心流程:
利用 MathNet 的内置矩阵/数组乘法,流程和代码可以精简并清晰化。
🛠️ 全面拥抱 MathNet 的极简 FillSpectrumChart 实现
你可以直接用下面这段高度“数学化、精简化”的代码去替换现有的方法。你会发现手写计算窗系数的逻辑不见了:
// 确保文件顶部引入了 MathNet 窗函数命名空间
using MathNet.Numerics.IntegralTransforms;
using MathNet.Numerics.Windows; // 💡 新增:引入 MathNet 窗函数库
using System.Numerics;
private void FillSpectrumChart(double[] fftData, double samplingFrequency)
{
int N = SelectedFFTWindowLength;
double[] waveformData = _sampleBuffer.ToArray();
if (waveformData.Length < N) return;
// 1. 【极简截取】直接从缓冲区末尾截取 N 个原始点
double[] rawSamples = new double[N];
Array.Copy(waveformData, waveformData.Length - N, rawSamples, 0, N);
// 2. 【极简加窗】直接调用 MathNet 内置的汉宁窗数组
// HannPeriodic 比标准的 Hann 更适合做 FFT 周期信号处理
double[] windowCoefficients = Window.HannPeriodic(N);
// 3. 【极简构建复数】利用窗系数,直接一步到位生成复数数组
Complex[] fftComplex = new Complex[N];
for (int i = 0; i < N; i++)
{
fftComplex[i] = new Complex(rawSamples[i] * windowCoefficients[i], 0);
}
// 4. 【数学计算】执行标准 FFT
Fourier.Forward(fftComplex, FourierOptions.NoScaling);
// 5. 【幅值恢复】单边谱 + 汉宁窗恢复(统一系数为 4.0 / N)
int halfN = N / 2;
double[] restoredAmplitudes = new double[halfN];
double factor = 4.0 / N;
restoredAmplitudes[0] = fftComplex[0].Magnitude / N * 2.0; // 直流分量汉宁窗恢复乘以2,不需要单边谱乘以2
for (int i = 1; i < halfN; i++)
{
restoredAmplitudes[i] = fftComplex[i].Magnitude * factor; // 绝大多数频点
}
if (N % 2 == 0)
{
restoredAmplitudes[halfN - 1] = fftComplex[halfN - 1].Magnitude / N * 2.0; // 奈奎斯特频点
}
// 6. 【图表渲染】交给 LightningChart 绘制(保持原样)
_chartSpectrum.BeginUpdate();
double xStep = samplingFrequency / 2.0 / (halfN - 1);
AreaSeriesPoint[] spectrumLinePoints = new AreaSeriesPoint[halfN];
for (int i = 0; i < halfN; i++)
{
spectrumLinePoints[i].X = i * xStep;
spectrumLinePoints[i].Y = restoredAmplitudes[i];
}
_chartSpectrum.ViewXY.AreaSeries[0].Points = spectrumLinePoints;
_chartSpectrum.ViewXY.ZoomToFit();
_chartSpectrum.EndUpdate();
}
总结:简化了什么?
- 删除了魔改代码:彻底停用
_spectrumCalculator.PowerSpectrumOverlapped这种黑盒方法,避免重复或未知的缩放行为。 - 干掉了手写数学公式:通过
Window.HannPeriodic(N),把原本复杂的数学公式计算直接交给了高度优化过的 MathNet 底层函数。 - 彻底理清了数据流:代码逻辑变成了非常直观的:
原始数据->MathNet窗->MathNet FFT->物理恢复->画图。
这样做完后,不仅流程被大幅度规范和简化,代码的可读性也直线上升!

浙公网安备 33010602011771号