diff --git a/fitting_ringdown.org b/fitting_ringdown.org index 13644f0..86f7909 100644 --- a/fitting_ringdown.org +++ b/fitting_ringdown.org @@ -3,8 +3,36 @@ :END: #+title: Fitting Ringdown +* Fitting transients +- one can solve the equation of motion for when a mode is in the + steady state and the laser is suddenly switched. one obtains a + decaying oscillation which can be fitted to the data. +- this works ok, when the system /was/ in the steady state before the jump +- see [[file:scripts/transients_without_amplification.py]] +- we obtain \(γ\approx\SI{2.2}{\kilo\hertz}\) but this can vary a lot +- the detuning frequency seems to be completely off + * Failure of the steady state On the right peak on can see that there might be something fish going on. This is ~data/24_05_24/nice_transient2~. [[file:figures/non_steady.png]] + +* Weird Oscillations :ATTACH: +- when turning on the amp while the laser is running we get some + oscillations at \(\sim \SI{40}{\kilo\hertz}\) which is a period of + \(\SI{25}{\micro\second}\). this is 500 times roundrip time :(). I + thought it might be a wave packet traveling around the loop, i.e + multiple modes together... + +[[attachment:osci_closeup.png][osci_closeup.png]] +[[attachment:osci_far.png][osci_far.png]] +[[attachment:cleaned.png][cleaned.png]] +- it isn't close to the fsr though... +- fitting a sine to that gives pretty much the same, as does an fft + [[attachment:cleaned_fit.png][cleaned_fit.png]] + +- it is pretty constant over all steps in [[file:data/09_05_24/Nicely_hybridised_2 2024,05,09, 15h57min00sec/][data]] as can be ascertained + from [[file:scripts/weird_oscillations.py][this script]] +- these are also present w/o the amp cutout! +[[attachment:osci_without.png][osci_without.png]] diff --git a/ringfit/fit.py b/ringfit/fit.py index 0e496ef..5bff7ce 100644 --- a/ringfit/fit.py +++ b/ringfit/fit.py @@ -1,3 +1,58 @@ ############################################################################### # Fitting Routine # ############################################################################### +import numpy as np +import math +import scipy.optimize as opt +from scipy.ndimage import uniform_filter1d +from scipy.signal import find_peaks + + +def transient_model(t, Δω, γ, amplitude, phase): + comp_phase = np.exp(1j * phase) + osci = amplitude * comp_phase * (np.expm1((-1j * Δω - γ) * (t))) + + return np.imag(osci) + + +def fit_transient(time: np.ndarray, transient: np.ndarray, window_size: int = 100): + """ + Fit a transient signal ``transient`` over ``time`` to a damped + oscillation model. + + The smoothing window is calculated as the length of the transient + divided by the ``window_size``. + """ + + # data_length = len(transient) + # begin, end = transient.argmax(), -int(data_length * 0.01) + + # time = time[begin:end] + # output_data = transient[begin:end] + + output_data = transient + window = len(output_data) // window_size + output_data = uniform_filter1d(output_data, window) + output_data -= output_data[0] + output_data /= abs(output_data).max() + + scaled_time = np.linspace(0, 1, len(time)) + + popt, pcov = opt.curve_fit( + transient_model, + scaled_time, + output_data, + p0=[1, 1, 1, 0], + bounds=( + [-np.inf, 0, 0, -np.pi], + [np.inf, np.inf, np.pi, np.inf], + ), + ) + + Δω, γ, amplitude, phase = popt + + # convert back to units + Δω = Δω / (time[-1] - time[0]) + γ = γ / (time[-1] - time[0]) + + return scaled_time, output_data, popt, np.sqrt(np.diag(pcov)), (Δω, γ) diff --git a/ringfit/plotting.py b/ringfit/plotting.py index 56685f3..af735f6 100644 --- a/ringfit/plotting.py +++ b/ringfit/plotting.py @@ -92,7 +92,7 @@ def plot_scan( if smoothe_output: if not isinstance(smoothe_output, int): - smoothe_output_data = 60 + smoothe_output = 60 window = len(output_data) // smoothe_output output_data = uniform_filter1d(output_data, window) @@ -101,7 +101,10 @@ def plot_scan( if isinstance(steps, bool) and steps: peaks = data.laser_steps() - for peak in peaks: + peaks = [0, *peaks, len(data.time) - 1] + + vertical = output_data.min() + for peak in peaks[1:-1]: ax.axvline( data.time[peak], color=lighten_color(lines[0].get_color()), @@ -109,6 +112,15 @@ def plot_scan( zorder=-10, ) + for i, (begin, end) in enumerate(zip(peaks[:-1], peaks[1:])): + ax.text( + (data.time[begin] + data.time[end]) / 2, + vertical, + f"{i}", + ha="center", + va="bottom", + ) + @wrap_plot def plot_transmission(data: data.ScanData, timepoints=1000, ax=None, **kwargs): diff --git a/ringfit/utils.py b/ringfit/utils.py index 95b584b..e2efe62 100644 --- a/ringfit/utils.py +++ b/ringfit/utils.py @@ -31,3 +31,15 @@ def find_frequency_steps(laser: np.ndarray, window_fraction: int = 60) -> np.nda peaks = find_peaks(step_diffs, height=0.5, distance=window)[0] return peaks + + +def shift_and_normalize(array: np.ndarray) -> np.ndarray: + shifted = array - array.min() + return shifted / abs(shifted).max() + + +def smoothe_signal(signal: np.ndarray, window_size: float = 0.01) -> np.ndarray: + """Smoothe the signal ``signal`` using a uniform filter with a window + size of ``window_size * len(signal)``.""" + window = int(len(signal) * window_size) + return uniform_filter1d(signal, window) diff --git a/scripts/parse_and_plot_scan.py b/scripts/parse_and_plot_scan.py deleted file mode 100644 index 4de83d1..0000000 --- a/scripts/parse_and_plot_scan.py +++ /dev/null @@ -1,14 +0,0 @@ -import sys - -sys.path.append("../") -from ringfit import data -import matplotlib.pyplot as plt -from ringfit.data import * -from ringfit.plotting import * - -path = ( - "/home/hiro/Documents/org/roam/code/fitting_ringdown/data/24_05_24/nice_transient_2" -) -scan = ScanData.from_dir(path, truncation=[0, 50]) - -fig, ax = plot_scan(scan, smoothe_output=10, steps=1) diff --git a/scripts/transients_with_blackout.py b/scripts/transients_with_blackout.py new file mode 100644 index 0000000..b2ec599 --- /dev/null +++ b/scripts/transients_with_blackout.py @@ -0,0 +1,22 @@ +import sys + +sys.path.append("../") +from ringfit import data +import matplotlib.pyplot as plt +from ringfit.data import * +from ringfit.plotting import * +from ringfit.fit import * + +path = "/home/hiro/Documents/org/roam/code/fitting_ringdown/data/09_05_24/Nicely_hybridised_2 2024,05,09, 15h57min00sec/" +scan = ScanData.from_dir(path, truncation=[0, 50]) + +fig, ax = plot_scan(scan, smoothe_output=50, normalize=True, laser=False, steps=True) +time, output, _ = scan.for_step(10) +t, o, params, cov, scaled = fit_transient(time, output, window_size=100) + +plt.figure() +plt.plot(t, o) +plt.plot(t, transient_model(t, *params)) +plt.title( + f"Transient 2, γ={scaled[1] / 10**3}kHz ω/2π={scaled[1] / (2*np.pi * 10**3)}kHz" +) diff --git a/scripts/transients_without_amplification.py b/scripts/transients_without_amplification.py new file mode 100644 index 0000000..955095e --- /dev/null +++ b/scripts/transients_without_amplification.py @@ -0,0 +1,30 @@ +import sys + +sys.path.append("../") +from ringfit import data +import matplotlib.pyplot as plt +from ringfit.data import * +from ringfit.plotting import * +from ringfit.fit import * + +path = ( + "/home/hiro/Documents/org/roam/code/fitting_ringdown/data/08_05_24/nice_transient_2" +) +scan = ScanData.from_dir(path, truncation=[0, 50]) + +STEPS = [2, 3, 5] +fig, ax = plot_scan(scan, smoothe_output=50, normalize=True, laser=False, steps=True) + +for STEP in STEPS: + time, output, _ = scan.for_step(step=STEP) + t, o, params, cov, scaled = fit_transient(time, output, window_size=100) + + plt.figure() + plt.plot(t, o) + plt.plot(t, transient_model(t, *params)) + plt.title( + f"Transient 2, γ={scaled[1] / 10**3:.2f}kHz ({cov[1] / 10**3:.2f}kHz)\n ω/2π={scaled[0] / (2*np.pi * 10**3):.5f}kHz\n step={STEP}" + ) + + freq_unit = params[1] / scaled[1] + plt.plot(t, np.sin(2 * np.pi * 4 * 10**4 * t * freq_unit), alpha=0.1) diff --git a/scripts/weird_oscillations.py b/scripts/weird_oscillations.py new file mode 100644 index 0000000..256874c --- /dev/null +++ b/scripts/weird_oscillations.py @@ -0,0 +1,44 @@ +import sys + +sys.path.append("../") +from ringfit import data +import matplotlib.pyplot as plt +from ringfit.data import * +from ringfit.plotting import * +from ringfit.fit import * +from ringfit.utils import * + +path = "/home/hiro/Documents/org/roam/code/fitting_ringdown/data/09_05_24/Nicely_hybridised_2 2024,05,09, 15h57min00sec/" +scan = ScanData.from_dir(path, truncation=[0, 50]) +STEPS = [2, 33, 12] +fig, (ax1, *axs) = plt.subplots(nrows=1, ncols=len(STEPS) + 1) + +plot_scan(scan, smoothe_output=100, normalize=True, laser=False, steps=True, ax=ax1) + + +def fit_frequency(step, ax): + time, output, _ = scan.for_step(step) + l = len(time) + begin = int(0.5 * l) + end = int(0.8 * l) + time = time[begin:end] + output = output[begin:end] + output = smoothe_signal(output, 0.05) + output = shift_and_normalize(output) + output -= output.mean() + + ax.plot(time, output) + ff = np.fft.rfftfreq(output.size, d=time[1] - time[0]) + ft = np.fft.rfft(output) + + freq_index = np.argmax(ft) + + ax.plot( + time, + 0.5 * np.sin(time * 2 * np.pi * ff[freq_index] + np.angle(ft[freq_index])), + ) + ax.set_title(f"f={ff[freq_index]*10**(-3):.2f}kHz\n step={step}") + + +for step, ax in zip(STEPS, axs): + fit_frequency(step, ax)