我如何知道数组是否代表正弦波?

时间:2017-02-05 08:21:21

标签: arrays swift algorithm sine

我使用加速度计并在过去的几秒钟内收集数据。我想要检测的运动可以表示为从运动传感器得到的值I' s的正弦波。所以为了确保,我想要一种方法来检查从传感器返回的数据是否代表正弦波。

我想避免的是手动比较数组中的每个值并做出决定。

我想知道是否有一种有效的方法可以判断我的数组是否代表正弦波。

1 个答案:

答案 0 :(得分:8)

正如评论员@NeilForrester指出的那样,FFTs是这样做的方法。编写自己的高效FFT并不容易,但Accelerate框架vDSP routines提供了一种直接的方法来实现它如果你使用的是Objective-C - 不是那么简单Swift,由于使用了UnsafePointerUnsafeMutablePointer参数。这是一个使用FFT的简单Swift示例。

import Foundation
import Accelerate

public struct GFFT {
    let size: Int
    let halfSize: Int
    let log2n: vDSP_Length
    let twoOverSize: [Float]
    var weights: FFTSetup

    init?(size: Int) {
        self.size = size
        self.log2n = vDSP_Length(log2(Float(size)))
        guard let weights = vDSP_create_fftsetup(log2n, FFTRadix(kFFTRadix2)) else {
            print("Aargh in GFFT.fft - weights failed")
            return nil
        }
        self.halfSize = size / 2
        self.twoOverSize = [2 / Float(size)]
        self.weights = weights
    }

    public func forward(realArray: [Float]) -> (magnitude: [Float], phase: [Float]) {
        assert(realArray.count == self.size, "Aargh in GFFT.forward - size mismatch")
        var real = realArray // copy into var
        var imag = GFFT.zeros(size)
        var magnitudesSquared = GFFT.zeros(self.halfSize)
        var magnitudes = GFFT.zeros(self.halfSize)
        var normalizedMagnitudes = GFFT.zeros(self.halfSize)
        var phases = GFFT.zeros(self.halfSize)

        var splitComplex = DSPSplitComplex(realp: &real, imagp: &imag)

        vDSP_fft_zip(self.weights, &splitComplex, 1, self.log2n, FFTDirection(FFT_FORWARD))

        vDSP_zvmags(&splitComplex, 1, &magnitudesSquared, 1, vDSP_Length(self.halfSize))
        vvsqrtf(&magnitudes, &magnitudesSquared, [Int32(self.halfSize)])

        vDSP_zvphas(&splitComplex, 1, &phases, 1, vDSP_Length(self.halfSize))

        vDSP_vsmul(&magnitudes, 1, self.twoOverSize, &normalizedMagnitudes, 1, vDSP_Length(self.halfSize))

        // you may choose to return magnitudesSquared, for the power
        // magnitudes for the scaled amplitudes or 
        // normalizedMagnitudes for, well, normalised magnitude.
        return (normalizedMagnitudes, phases)
    }
    private static func zeros(_ n: Int) -> [Float] { return [Float](repeating: 0, count: n) }
}

let testInput = (0 ..< 512).map {
    return sin(Float($0))
}
if let fft = GFFT(size: testInput.count) {
    let (freq, phase) = fft.forward(realArray: testInput)
    freq.map({$0})
}

游乐场输出:

FFT graph

至于你测试的是什么,它取决于你得到的实际输出,所以我会试验实际数据给你的东西,但你的测试应该是这样的:

  • 找到幅度的平均值
  • 找到最大幅度
  • 检查两者的比率(最大/平均值)是否高
  • 检查最大值的索引是否接近零(或者可能是DC信号)
  • 检查是否有任何其他局部最大值(并且会有一些)远小于全局最大值。