XamlGuy - 4 years ago 101
Swift Question

# How do I know if an array represents sine wave?

I'm working with accelerometer and collecting data for past few seconds. The motion I want to detect can be represented as sine wave in terms of values I'm getting from motion sensor. So to make it sure, I want a way to check if the data returned from sensor do represents a sine wave.

What I want to avoid is to manually compare each value in array and draw a decision.

I'm wondering if there's an efficient way which could tell if my array represents a sine wave or not.

As commentator @NeilForrester points out, FFTs are the way to do this. Writing your own efficient FFT is not easy, but the `Accelerate` framework's vDSP routines provide a straight-forward way to do it if you are using Objective-C - not so straightforward in Swift, due to the use of `UnsafePointer` and `UnsafeMutablePointer` parameters. Here's a simple Swift example of using the FFT.

``````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)))
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})
}
``````

Playground output:

As to what you test, it will depend on the actual outputs you get, so I would experiment with what the actual data gives you, but your test should be something like :

• find the mean of the amplitudes
• find the maximum amplitude
• check that the ratio of the two (max / mean) is high
• check that the index of the maximum isn't near zero (or it's likely a DC signal)
• check that any other local maxima (and there will be some) are much smaller than the global maximum.
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download