我怎么知道数组是否代表正弦波?
How do I know if an array represents sine wave?
我正在使用加速度计并收集过去几秒钟的数据。我想要检测的运动可以根据我从运动传感器获得的值表示为正弦波。所以为了确定,我想要一种方法来检查从传感器返回的数据是否代表正弦波。
我想避免的是手动比较数组中的每个值并做出决定。
我想知道是否有一种有效的方法可以判断我的阵列是否代表正弦波。
正如评论员@NeilForrester 指出的那样,FFTs are the way to do this. Writing your own efficient FFT is not easy, but the Accelerate
framework's vDSP routines 提供了一种 straight-forward 方法 如果您正在使用 Objective-C - 并非如此由于使用了 UnsafePointer
和 UnsafeMutablePointer
参数,在 Swift 中很简单。这是使用 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([=10=]))
}
if let fft = GFFT(size: testInput.count) {
let (freq, phase) = fft.forward(realArray: testInput)
freq.map({[=10=]})
}
游乐场输出:
至于你测试什么,这将取决于你得到的实际输出,所以我会用实际数据给你做实验,但你的测试应该是这样的:
- 求振幅的平均值
- 找到最大幅度
- 检查两者的比率(最大值/平均值)是否很高
- 检查最大值的索引是否不接近零(或者可能是直流信号)
- 检查任何其他局部最大值(并且会有一些)是否比全局最大值小得多。
我正在使用加速度计并收集过去几秒钟的数据。我想要检测的运动可以根据我从运动传感器获得的值表示为正弦波。所以为了确定,我想要一种方法来检查从传感器返回的数据是否代表正弦波。
我想避免的是手动比较数组中的每个值并做出决定。
我想知道是否有一种有效的方法可以判断我的阵列是否代表正弦波。
正如评论员@NeilForrester 指出的那样,FFTs are the way to do this. Writing your own efficient FFT is not easy, but the Accelerate
framework's vDSP routines 提供了一种 straight-forward 方法 如果您正在使用 Objective-C - 并非如此由于使用了 UnsafePointer
和 UnsafeMutablePointer
参数,在 Swift 中很简单。这是使用 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([=10=]))
}
if let fft = GFFT(size: testInput.count) {
let (freq, phase) = fft.forward(realArray: testInput)
freq.map({[=10=]})
}
游乐场输出:
至于你测试什么,这将取决于你得到的实际输出,所以我会用实际数据给你做实验,但你的测试应该是这样的:
- 求振幅的平均值
- 找到最大幅度
- 检查两者的比率(最大值/平均值)是否很高
- 检查最大值的索引是否不接近零(或者可能是直流信号)
- 检查任何其他局部最大值(并且会有一些)是否比全局最大值小得多。