# Identify which of the three signals is closest to a sinusoidal curve

I have some acceleration values from a sensor. These values are reported as x, y and z components of the acceleration. As can be seen in the attached figure, it is clear that the z signal has a clear sinusoidal curve present. Is there any algorithm to quantify the "sinusoidalness" of a curve. I am trying to write an algorithm that would identify the z signal of the attached image to be the most sinusoidal.

Another example is shown in the figure below: For this image the x-axis contains the < most sinusoidal > signal and the algorithm must be able to identify the x-axis as the correct axis.

Edit: The variation could indeed be greater in any of the axes without the most sinusoidal signal. The original problem being solved here is to count the number of breaths in the accelerometer signal. To do this first the axis containing the breathing signal must be identified. From the image it is obvious that for the first image the z-axis has the breathing data and for the second image the x-axis has the breathing data

• Welcome to SE.SP! Does it matter that the $y$ plot has a non-zero offset? Does it matter that the variation in $y$, even after removing the mean, is much greater than the variation in $z$? Please update your question with this information.
– Peter K.
May 26 '21 at 15:25
• @PeterK. I have updated the question in form of an edit at the bottom. Hope the question is clearer now. Thanks! May 27 '21 at 7:27
• Do you have the possibility to share your datasets May 27 '21 at 17:27

Is it sufficient to identify the «most sinuoidal» of those 3, or would you also want linear projections of those (consistent with a IMU sensor tilted vs the plane of motion)?

A simple solution might be to do a windowed fft and pick the direction where the «crest factor» of the fft magnitude was largest (best explained by a single sinoid).

Edit: It appears thah your «sine» is somewhat variable. If the above approach fails, then perhaps removing the DC component («highpass filter») followed by removing the high frequency edges and impulses (lowpass filter) for an effective tuned bandpass filter, followed by squaring and summing the samples could be used.

• Thanks! Identification of <most sinusoidal> is indeed enough for my problem. Once the <most sinusoidal> axis is identified, I have an algorithm to count the number of peaks and valleys in it(i.e. the number of breaths in that signal). The signals shown in the question image have already been through a band-pass Butterworth filter to remove any non realistic low and high frequency noise. The <sine> can indeed be somewhat variable as the signal doesn't always contain only breathing data. The goal is to identify which one of these curves is the most <sine> like. May 27 '21 at 7:36

Why not just take the FFT and see which one has the highest peak?

The code below generates example data:

and then takes the FFT of it:

which yields:

X sum: 0.9999999999999987 Y sum:0.9999999999999994 Z sum:0.9999999999999989

X max: 0.17213933316891214 Y max:0.2080419608439683 Z max:0.7112824350827284

Depending on your data, that might be enough to pick the most sinusoidal one.

## Python code only below

import numpy
import matplotlib.pyplot as plt
import scipy.signal as sig
from scipy.fft import fft
import random

def get_noise(N):
noise = []
for i in range(N):
noise.append(random.gauss(0,1))
return noise

def normalize(x):
return [x_1/sum(numpy.abs(x)) for x_1 in x]

N = 1000
b, a = sig.butter(3, 0.25)
n1 = get_noise(N)
n2 = get_noise(N)
n3 = get_noise(N)
x = normalize(sig.lfilter(b,a, n1))
y = normalize(sig.lfilter(b,a, n2))
t = [ t*0.05 for t in range(N) ]
z = normalize(sig.lfilter(b,a, n3) + numpy.sin(t))

figure, axis = plt.subplots(3, 1)
axis[0].plot(x)
axis[1].plot(y)
axis[2].plot(z)

plt.figure(2)
plt.plot(numpy.abs(fft(x)))
plt.plot(numpy.abs(fft(y)))
plt.plot(numpy.abs(fft(z)))

print("X sum: " + str(sum(numpy.abs(x))) + " Y sum:" + str(sum(numpy.abs(y))) + " Z sum:"+ str(sum(numpy.abs(z))))
print("X max: " + str(max(numpy.abs(fft(x)))) + " Y max:" + str(max(numpy.abs(fft(y)))) + " Z max:"+ str(max(numpy.abs(fft(z)))))

• I like the answer but what if we have a case of white noise with a higher spectral density than a pure but much lower power sine wave? What would the best metric be to work in that possible condition? To say a signal is closest to a sinusoidal curve suggests to me a complex conjugate FT with no interference - so an algorithm to detect both complex conjugate peaks and determine the SNR would be ideal to compare metics I think - thoughts? May 28 '21 at 3:10
• @DanBoschen The higher the noise level, the more you possibly need to know about the signal (e.g. the approximate frequency). Judging (by eyeball) from the OP's posts, the noise isn't white and isn't very high compared with the sinusoid, hence my stupidly simple approach. With your constraints, I'd think about only looking at a specified range of FFT bins (if I had a frequency range). The FFT or its variants are still, mostly, the best bang-for-buck when finding a sinusoid in noise.
– Peter K.
May 28 '21 at 14:32

It looks like your signal is sine-like, but not a single frequency. If this is typical, then you could begin by making assumptions about the range of frequencies it will contain, do an FFT, and then pick the channel that has the most energy in that band by summing the magnitudes of the frequency bins in that range. You might also want to set a threshold for this result so that you don't identify random noise as a signal occupying the band. It would be helpful if you provided more background on what you expect the signal to be... frequency range, noise level, appearance on more than one channel, etc.