Interactive FFT with an intervalslider in Makie

Hello, I have taken a relatively large amount of readings over time and would now like to look at them in detail. For this purpose I use an interval slider in Makie with which I can zoom into my data. This works already very well.
In the next step I want to run a fast fourier transformation dynamically over the selected area. This should be adjusted with the slider position. I’ve played around a bit, but haven’t made it to work yet. Maybe someone of you has an idea.

intSh1 = IntervalSlider(fig[2, 1], range = collect(1:1:length(test_time)), startvalues = (1, length(test_time)), linewidth = 20)

Label(fig[2, 1], @lift(string(test_time[$(intSh1.interval)[1]]) * " sec bis " * string(test_time[$(intSh1.interval)[2]]) * " sec"), tellwidth = false)

vlines!(ax0, @lift(test_time[$(intSh1.interval)[1]]), ymin = -2.0, ymax = 2.0, color = :red, linewidth = 2)
vlines!(ax0, @lift(test_time[$(intSh1.interval)[2]]), ymin = -2.0, ymax = 2.0, color = :red, linewidth = 2)

F = rfft(volt) |> fftshift
freqs = rfftfreq(length(test_time), 1/(steptime*1e-3)) |> fftshift

new_fft = lift(intSh1.interval) do val
    F = rfft(volt[val[1]:val[2]]) |> fftshift
    freqs = rfftfreq(length(test_time[val[1]:val[2]]), 1/(steptime*1e-3)) |> fftshift
end

new_lims = lift(intSh1.interval) do val
    limits!(ax1, FRect(test_time[val[1]], -0.75, test_time[val[2]] - test_time[val[1]], 1.5))
end

lines!(ax0, test_time, volt, linewidth = 0.5, color = :blue)
lines!(ax1, test_time, volt, linewidth = 1, color = :blue)
lines!(ax2, freqs, abs.(F), linewidth = 1, color = :blue)

display(fig)

1 Like
using FFTW, GLMakie

function interactive(data, timestep)

    N = size(data, 1)

    time = range(0, step=timestep, length=N) |> collect

    fig = Figure(resolution=(2400, 1400))

    ax0 = fig[1,1] = Axis(fig)

    intSh1 = fig[2,1] = IntervalSlider(
            fig,
            range = 1:N,
            startvalues = (1, N),
            linewidth = 20)
    fig[2, 1] = Label(
            fig,
            @lift(string(time[$(intSh1.interval)[1]]) * " sec bis " * string(time[$(intSh1.interval)[2]]) * " sec"),
            tellwidth = false)

    ax1 = fig[3,1] = Axis(fig)
    ax2 = fig[4,1] = Axis(fig)

    vlines!(ax0, @lift(time[$(intSh1.interval)[1]]), ymin = -2.0, ymax = 2.0, color = :red, linewidth = 2)
    vlines!(ax0, @lift(time[$(intSh1.interval)[2]]), ymin = -2.0, ymax = 2.0, color = :red, linewidth = 2)

    # The plotted values need to be observables so they update on change, and they must
    # be synchronized.
    # https://makie.juliaplots.org/stable/interaction/nodes.html#Problems-With-Synchronous-Updates
    new_freqs = Node(rfftfreq(N, 1/timestep)|>fftshift|>collect)
    new_F = lift(intSh1.interval) do val
        new_freqs.val = rfftfreq(val[2]-val[1]+1, 1/timestep) |> fftshift |> collect
        rfft(data[val[1]:val[2]]) |> fftshift .|> abs
    end

    new_lims = lift(intSh1.interval) do val
        limits!(ax1, FRect(time[val[1]], -0.75, time[val[2]] - time[val[1]], 1.5))
    end

    lines!(ax0, time, data, linewidth = 0.5, color = :blue)
    lines!(ax1, time, data, linewidth = 1, color = :blue)
    lines!(ax2, new_freqs, new_F, linewidth = 1, color = :blue)

    display(fig)

end
1 Like

Thank you for the quick reply. Unfortunately I still do not understand the functions of |>
There is a function applied to the argument before it or? What is the advantage in this syntax? If I have understood it correctly are sum([1:5;]) and [1:5;] |> sum the same or?

Thanks and sorry if it’s a very stupid question :wink:

Yes, it’s just a stylistic question. Sometimes a sequence of functions being applied is easier to grasp than reading inside out func3(func2(func1(x)))