使用vDSP_zaspec计算信号自谱异常,寻求技术帮助
问题分析与解决方案
你的代码存在两个关键错误,导致vDSP_zaspec输出不符合预期:
- 输入不是频域复数序列:
vDSP_zaspec的作用是计算频域复数序列的自功率谱,而你直接把原始时域信号作为实部、虚部全0传入,本质上是把时域信号当作频域输入,完全不符合函数设计逻辑。必须先对输入信号做FFT,得到频域的复数表示后再传入该函数。 - 输出数组长度参数错误:根据文档,输入
DSPSplitComplex的长度为N时,输出自谱数组的长度应为N+1(对应从0到Nyquist频率的所有频率点),你设置的输出长度和输入信号长度一致,会导致内存访问问题或结果异常。
修正后的代码
class func autoSpectrum(input: [Float]) -> [Float] { let inputCount = input.count // 确保FFT输入长度为2的幂(FFT算法要求,若输入已满足可跳过补零) let fftLength = vDSP_Length(inputCount.nextPowerOfTwo()) let log2FFTLength = vDSP_Length(log2(Float(fftLength))) // 初始化时域转频域的缓冲区 var realBuffer = [Float](input) // 补零到2的幂长度 if realBuffer.count < Int(fftLength) { realBuffer.append(contentsOf: [Float](repeating: 0.0, count: Int(fftLength) - realBuffer.count)) } var imagBuffer = [Float](repeating: 0.0, count: Int(fftLength)) var splitComplex = DSPSplitComplex(realp: &realBuffer, imagp: &imagBuffer) // 创建FFT配置并执行正向FFT(时域→频域) let fftSetup = vDSP_create_fftsetup(log2FFTLength, FFTRadix(kFFTRadix2))! defer { vDSP_destroy_fftsetup(fftSetup) } vDSP_fft_zip(fftSetup, &splitComplex, 1, log2FFTLength, FFTDirection(FFT_FORWARD)) // 初始化自谱输出数组,长度为fftLength + 1 var autoSpectrumResult = [Float](repeating: 0.0, count: Int(fftLength) + 1) // 计算自功率谱 vDSP_zaspec(&splitComplex, &autoSpectrumResult, fftLength) // 归一化处理(按需调整) let rmsValue = vDSP.rootMeanSquare(autoSpectrumResult) if rmsValue > 0 { vDSP.divide(autoSpectrumResult, rmsValue, result: &autoSpectrumResult) } return autoSpectrumResult }
关键说明
- FFT要求输入长度为2的幂,代码中用
nextPowerOfTwo()自动处理,若你的输入信号长度已经满足该条件,可以移除补零逻辑。 - 修正后,用500Hz正弦波测试时,你会在对应频率的位置看到明显的功率峰值,符合自谱的预期结果。
vDSP_zaspec输出的是实数值数组,每个元素对应一个频率点的自功率。
内容的提问来源于stack exchange,提问作者Jeff 974
相关产品推荐
相关产品推荐

