If the pulses are periodic, another idea would be to fit a model to the noisy data and invert for the exponential time constants in one go.
The example below fits 6 parameters: period, time-shift, max and min voltages, and the exponentials raise and fall time constants:
LsqFit code
# 1 - GENERATE INPUT SIGNAL WITH NOISE
function expsignal(t, T, t0, V1, V2, a, b)
raise(t,t0,V1,V2) = (t<t0 || t>=t0+T/2) ? 0.0 : V1 + (V2-V1)*(1 - exp(-a*(t-t0)))
fall(t,t0,V1,V2) = (t<t0 || t>=t0+T/2) ? 0.0 : V1 + (V2-V1)*exp(-b*(t-t0))
n = round(Int, length(t)/T)
y = zero(t)
for i in 0:n
y += raise(t, t0 + 2*(i-1)*T/2, V1, V2) + fall(t, t0 + (2*i-1)*T/2, V1, V2)
end
return y
end
T = 3e-3 # Period [s]
V1 = 0.10 # Min signal amplitude [V]
V2 = 0.45 # Max signal amplitude [V]
σN = 0.02 # Noise level
α = 2.0e4 # 1/raise time constant [1/s]
β = 5.0e4 # 1/fall time constant [1/s]
dt = 5e-7 # sampling rate [s]
t = 0:dt:(3*T - dt) # time interval sampled
t0 = T*rand() # random start time [s]
N = length(t)
y = expsignal.(t, T, t0, V1, V2, α, β) + σN*(rand(N) .- 0.5)
# 2 - ANALYSE DATA AND FIT PERIODIC EXPONENTIAL SIGNAL
using FFTW, Statistics, LsqFit
ix0 = findmax(abs.(fft(y .- mean(y))[1:N÷2]))[2]
T0 = (N*dt)/(ix0-1) # approximate main cycle period from FFT
V1 = maximum(y)
V2 = minimum(y)
α₀ = 1.0e5 # [1/s] need educated initial guess (chose high value)
β₀ = 1.0e5 # [1/s] need educated initial guess (chose high value)
P0 = [T0, t0, V1, V2, α₀, β₀]
@. model(t, p) = expsignal(t, p[1], p[2], p[3], p[4], p[5], p[6])
fit = curve_fit(model, t, y, P0)
# 3 - DISPLAY RESULTS
println("Period [s] = ", round(coef(fit)[1]; sigdigits=4))
println("Time-shift [s] = ", round(coef(fit)[2]; sigdigits=4))
println("Minimum Voltage [V] = ", round(coef(fit)[3]; sigdigits=3))
println("Maximum Voltage [V] = ", round(coef(fit)[4]; sigdigits=3))
println("1/Fall time constant [1/s] = ", round(coef(fit)[5]; sigdigits=3))
println("Fall time constant [s] = ", round(1/coef(fit)[5]; sigdigits=3))
β₁ = round(coef(fit)[6]; sigdigits=3)
println("1/Raise time constant [1/s] = ", β₁)
τᵦ = round(1/coef(fit)[6]; sigdigits=3)
println("Raise time constant [s] = ", τᵦ)
using Plots
yt = model(t,fit.param)
plot(t, y, c=:blues)
τᵦ = round(1/β; sigdigits=5)
plot!(t, yt, c=:darkblue, lw=0.5, title = "LsqFit: fall time constant, τ = $τᵦ [s], inverse = $β₁ [1/s]", titlefont=8)