/// <summary> /// 计算时域信号的复数频谱,包含幅度谱和相位谱信息 /// </summary> /// <param name="waveform">时域波形数据</param> /// <param name="windowType">窗类型</param> /// <param name="spectrum">计算后的复数频谱数据</param> public static void AdvanceComplexFFT(double[] waveform, WindowType windowType, ref Complex[] spectrum) { if (waveform == null || spectrum == null || spectrum.Length < (waveform.Length / 2 + 1)) { throw new JYDSPUserBufferException(); } int n = waveform.Length, windowsize = waveform.Length; //做FFT的次数 int fftsize = windowsize; //做FFT点数 double cg = 0, enbw = 0; double[] xInTmp = null; double[] windowData = null; xInTmp = new double[fftsize]; GCHandle gchXIn = GCHandle.Alloc(waveform, GCHandleType.Pinned); var xInPtr = gchXIn.AddrOfPinnedObject(); GCHandle gchXout = GCHandle.Alloc(spectrum, GCHandleType.Pinned); var xOutPtr = gchXout.AddrOfPinnedObject(); try { //生成窗函数的数据 windowData = new double[windowsize]; Window.GetWindow(windowType, ref windowData, out cg, out enbw); CBLASNative.cblas_dscal(windowsize, 1 / cg, windowData, 1); //窗系数归一化 CBLASNative.cblas_dscal(spectrum.Length, 0, xOutPtr, 1); //将xOut清零 /*TIME_DOMAIN_WINDOWS(windowType, x_in, &CG, &ENBW, windowsize);*//*(double*)(xIn + i * windowsize)*/ VMLNative.vdMul(windowsize, windowData, xInPtr, xInTmp); BasicFFT.RealFFT(xInTmp, ref spectrum); } finally { gchXIn.Free(); gchXout.Free(); } }
/// <summary> /// 计算时域信号的复数频谱,包含幅度谱和相位谱信息 /// </summary> /// <param name="waveform">时域波形数据</param> /// <param name="spectralLines">频谱线的条数</param> /// <param name="windowType">窗类型</param> /// <param name="spectrum">计算后的复数频谱数据</param> /// <param name="spectralInfo">返回的频谱数据的参数信息</param> public static void AdvanceComplexFFT(double[] waveform, int spectralLines, WindowType windowType, double[] spectrum, ref SpectralInfo spectralInfo) { int n = waveform.Length, windowsize = 0, fftcnt = 0; //做FFT的次数 int fftsize = 0; //做FFT点数 double cg = 0, enbw = 0, scale = 0.0; double[] xInTmp = null; double[] windowData = null; Complex[] xOutCTmp = null; //输入的线数超过最大支持的线数则使用最大支持线数 if (spectralLines > MaxSpectralLine) { spectralLines = MaxSpectralLine; } //输入的点数小于线数,则窗长度为N,先加窗再补零到2*spectralLines再做FFT if (n <= 2 * spectralLines) { windowsize = n; fftcnt = 1; } else { windowsize = 2 * spectralLines; fftcnt = n / (2 * spectralLines); } fftsize = 2 * spectralLines; //不管N与2*spectralLines的关系是怎么样,FFT的点数都应该为 2*spectralLines xInTmp = new double[fftsize]; xOutCTmp = new Complex[fftsize / 2 + 1]; if (n < (2 * spectralLines)) { //memset(x_in + N, 0, (fftsize - N) * sizeof(double)); //补零至spectralLines for (int i = n; i < fftsize; i++) { xInTmp[i] = 0; } } //memset(xOut, 0, spectralLines * sizeof(double)); //生成窗函数的数据 windowData = new double[windowsize]; Window.GetWindow(windowType, ref windowData, out cg, out enbw); CBLASNative.cblas_dscal(windowsize, 1 / cg, windowData, 1); //窗系数归一化 CBLASNative.cblas_dscal(spectrum.Length, 0, spectrum, 1); //将xOut清零 GCHandle gch = GCHandle.Alloc(waveform, GCHandleType.Pinned); var xInPtr = gch.AddrOfPinnedObject(); for (int i = 0; i < fftcnt; i++) { //拷贝数据到临时内存中 //memcpy(x_in, x + i * windowsize, fftsize * sizeof(double)); /*TIME_DOMAIN_WINDOWS(windowType, x_in, &CG, &ENBW, windowsize);*//*(double*)(xIn + i * windowsize)*/ VMLNative.vdMul(windowsize, windowData, xInPtr + i * fftsize * sizeof(double), xInTmp); BasicFFT.RealFFT(xInTmp, ref xOutCTmp); //计算FFT结果复数的模,复用x_in做中间存储 VMLNative.vzAbs(fftsize / 2 + 1, xOutCTmp, xInTmp); //每次计算结果累加起来 VMLNative.vdAdd(spectralLines, xInTmp, spectrum, spectrum); } scale = 2 * (1.0 / fftcnt) / Sqrt2; //双边到单边有一个二倍关系,输出为Vrms要除以根号2 //fftcnt次的频谱做平均 CBLASNative.cblas_dscal(spectralLines, scale, spectrum, 1); spectrum[0] = spectrum[0] / Sqrt2; //上一步零频上多除了根号2,这里乘回来(Rms在零频上不用除根号2,单双边到单边还是要乘2 ?) spectralInfo.spectralLines = spectralLines; spectralInfo.FFTSize = fftsize; spectralInfo.windowSize = windowsize; spectralInfo.windowType = windowType; gch.Free(); }