Skip to content

Instantly share code, notes, and snippets.

@dermesser
Created March 31, 2021 12:08
Show Gist options
  • Select an option

  • Save dermesser/4212a456f234c23ef3c51c1dfa1d5001 to your computer and use it in GitHub Desktop.

Select an option

Save dermesser/4212a456f234c23ef3c51c1dfa1d5001 to your computer and use it in GitHub Desktop.
Quite accurately estimate the four wave parameters for a (co)sine wave, using some simple statistics and FFT.
using DataFrames
import FFTW
import Statistics
"""
estimate_wave_params(df::DataFrame, xsym::Symbol, ysym::Symbol)
Use Fourier analysis and statistics to find the best monochromatic sine wave parameters for data.
`xsym` and `ysym` are the column symbols for the x and y columns within DataFrame `df`, respectively.
Returns array of [amplitude, circular frequency, phase shift (radians), vertical offset].
"""
function ewp(df::DF.DataFrame, xsym::Symbol, ysym::Symbol)
ydata = df[!, ysym]
xdata = df[!, xsym]
sample_rate = 1 / Statistics.mean(xdata[2:end] - xdata[1:end-1])
offset = Statistics.mean(ydata)
ampl = Statistics.mean(Statistics.quantile(abs.(ydata .- offset), 99/100)) - offset
fft = FFTW.rfft(ydata)
fftfreq = FFTW.rfftfreq(length(ydata), sample_rate)
# Determine dominant frequency and calculate phase shift.
maxfreqloc = argmax(abs.(fft))
maxfreq = fftfreq[maxfreqloc]
shift = mod(atan(imag(fft[maxfreqloc]), real(fft[maxfreqloc])) + pi/2, 2pi)
freq = maxfreq * 2pi
[ampl, freq, shift, offset]
end
@dermesser
Copy link
Copy Markdown
Author

image

Smaller wave fits perfectly -- bigger wave (green) is not a good sine so the fit is not that good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment