Rename cercalo to sercalo

This commit is contained in:
2019-10-08 11:25:47 +02:00
parent b24cf1f7ff
commit 916dc5bb45
17 changed files with 1319 additions and 586 deletions

50
matlab/active_damping.m Normal file
View File

@@ -0,0 +1,50 @@
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
freqs = logspace(1, 3, 1000);
% Load Plant
load('mat/plant.mat', 'sys', 'Gi', 'Zc', 'Ga', 'Gc', 'Gn', 'Gd');
% Integral Force Feedback
bode(sys({'Vch', 'Vcv'}, {'Uch', 'Ucv'}));
Kppf = blkdiag(-10000/s, tf(0));
Kppf.InputName = {'Vch', 'Vcv'};
Kppf.OutputName = {'Uch', 'Ucv'};
figure;
% Magnitude
ax1 = subaxis(2,1,1);
hold on;
plot(freqs, abs(squeeze(freqresp(G, freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [dB]');
hold off;
% Phase
ax2 = subaxis(2,1,2);
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(G, freqs, 'Hz'))), 'k-');
set(gca,'xscale','log');
yticks(-360:90:180);
ylim([-360 0]);
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
inputs = {'Uch', 'Ucv', 'Unh', 'Unv'};
outputs = {'Ich', 'Icv', 'Rh', 'Rv', 'Vph', 'Vpv'};
sys_cl = connect(sys, Kppf, inputs, outputs);
figure; bode(sys_cl({'Vph', 'Vpv'}, {'Uch', 'Ucv'}), sys({'Vph', 'Vpv'}, {'Uch', 'Ucv'}))

View File

@@ -75,3 +75,13 @@ xlabel('Frequency [Hz]'); ylabel('Magnitude [dB]');
hold off;
xlim([freqs(1), freqs(end)]);
legend('location', 'southeast');
% Save the Controller
Kd = c2d(K, 1e-4, 'tustin');
% The diagonal controller is accessible [[./mat/K_diag.mat][here]].
save('mat/K_diag.mat', 'K', 'Kd');

View File

@@ -4,99 +4,186 @@ clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
% Load Plant
% Effect of the Sercalo angle error on the measured distance by the Attocube
% <<sec:sercalo_angle_error>>
% To simplify, we suppose that the Newport mirror is a flat mirror (instead of a concave one).
load('mat/plant.mat', 'Gi', 'Zc', 'Ga', 'Gc', 'Gn', 'Gd');
% The geometry of the setup is shown in Fig. [[fig:angle_error_schematic_sercalo]] where:
% - $O$ is the reference surface of the Attocube
% - $S$ is the point where the beam first hits the Sercalo mirror
% - $X$ is the point where the beam first hits the Newport mirror
% - $\delta \theta_c$ is the angle error from its ideal 45 degrees
% Test
% We define the angle error range $\delta \theta_c$ where we want to evaluate the distance error $\delta L$ measured by the Attocube.
bodeFig({Ga*Zc*Gi}, struct('phase', true));
% TODO Huddle Test
% We load the data taken during the Huddle Test.
load('mat/data_huddle_test.mat', ...
't', 'Uch', 'Ucv', ...
'Unh', 'Unv', ...
'Vph', 'Vpv', ...
'Vch', 'Vcv', ...
'Vnh', 'Vnv', ...
'Va');
thetas_c = logspace(-7, -4, 100); % [rad]
% We remove the first second of data where everything is settling down.
% The geometrical parameters of the setup are defined below.
t0 = 1;
H = 0.05; % [m]
L = 0.05; % [m]
Uch(t<t0) = [];
Ucv(t<t0) = [];
Unh(t<t0) = [];
Unv(t<t0) = [];
Vph(t<t0) = [];
Vpv(t<t0) = [];
Vch(t<t0) = [];
Vcv(t<t0) = [];
Vnh(t<t0) = [];
Vnv(t<t0) = [];
Va(t<t0) = [];
t(t<t0) = [];
t = t - t(1); % We start at t=0
% #+NAME: fig:angle_error_schematic_sercalo
% #+CAPTION: Schematic of the geometry used to evaluate the effect of $\delta \theta_c$ on the measured distance $\delta L$
% #+RESULTS:
% [[file:figs/angle_error_schematic_sercalo.png]]
% The nominal points $O$, $S$ and $X$ are defined.
O = [-L, 0];
S = [0, 0];
X = [0, H];
% Thus, the initial path length $L$ is:
path_nominal = norm(S-O) + norm(X-S) + norm(S-X) + norm(O-S);
% We now compute the new path length when there is an error angle $\delta \theta_c$ on the Sercalo mirror angle.
path_length = zeros(size(thetas_c));
for i = 1:length(thetas_c)
theta_c = thetas_c(i);
Y = [H*tan(2*theta_c), H];
M = 2*H/(tan(pi/4-theta_c)+1/tan(2*theta_c))*[1, tan(pi/4-theta_c)];
T = [-L, M(2)+(L+M(1))*tan(4*theta_c)];
path_length(i) = norm(S-O) + norm(Y-S) + norm(M-Y) + norm(T-M);
end
% We then compute the distance error and we plot it as a function of the Sercalo angle error (Fig. [[fig:effect_sercalo_angle_distance_meas]]).
path_error = path_length - path_nominal;
figure;
plot(thetas_c, path_error)
set(gca,'xscale','log');
set(gca,'yscale','log');
xlabel('Sercalo angle error [rad]');
ylabel('Attocube measurement error [m]');
% #+NAME: fig:effect_sercalo_angle_distance_meas
% #+CAPTION: Effect of an angle error of the Sercalo on the distance error measured by the Attocube ([[./figs/effect_sercalo_angle_distance_meas.png][png]], [[./figs/effect_sercalo_angle_distance_meas.pdf][pdf]])
% [[file:figs/effect_sercalo_angle_distance_meas.png]]
% And we plot the beam path using Matlab for an high angle to verify that the code is working (Fig. [[fig:simulation_beam_path_high_angle]]).
theta = 2*2*pi/360; % [rad]
H = 0.05; % [m]
L = 0.05; % [m]
O = [-L, 0];
S = [0, 0];
X = [0, H];
Y = [H*tan(2*theta), H];
M = 2*H/(tan(pi/4-theta)+1/tan(2*theta))*[1, tan(pi/4-theta)];
T = [-L, M(2)+(L+M(1))*tan(4*theta)];
figure;
hold on;
plot(t, Vph, 'DisplayName', '$Vp_h$');
plot(t, Vpv, 'DisplayName', '$Vp_v$');
plot([-L, -L], [0, H], 'k-'); % Interferometer
plot([-L, 0.1*L], [H, H], 'k-'); % Reflector
plot(0.5*min(L, H)*[-cos(pi/4-theta), cos(pi/4-theta)], 0.5*min(L, H)*[-sin(pi/4-theta), sin(pi/4-theta)], 'k-'); % Tilt-Mirror
plot(0.5*min(L, H)*[-cos(pi/4), cos(pi/4)], 0.5*min(L, H)*[-sin(pi/4), sin(pi/4)], 'k--'); % Initial position of tilt mirror
plot([O(1), S(1), Y(1), M(1), T(1)], [O(2), S(2), Y(2), M(2), T(2)], 'r-');
plot([O(1), S(1), X(1), S(1), O(1)], [O(2), S(2), X(2), S(2), O(2)], 'b--');
hold off;
xlabel('Time [s]');
ylabel('Amplitude [V]');
xlim([t(1), t(end)]);
legend();
xlabel('X [m]'); ylabel('Y [m]');
axis equal
% Error due to DAC noise used for the Sercalo
load('./mat/plant.mat', 'Gi', 'Gc', 'Gd');
G = inv(Gd)*Gc*Gi;
% We compute the Power Spectral Density of the horizontal and vertical positions of the beam as measured by the 4 quadrant diode.
% Dynamical estimation:
% - ASD of DAC noise used for the Sercalo
% - Multiply by transfer function from Sercalo voltage to angle estimation using the 4QD
[psd_Vph, f] = pwelch(Vph, hanning(ceil(1*fs)), [], [], fs);
[psd_Vpv, ~] = pwelch(Vpv, hanning(ceil(1*fs)), [], [], fs);
freqs = logspace(1, 3, 1000);
fs = 1e4;
% ASD of the DAC voltage going to the Sercalo in [V/sqrt(Hz)]
asd_uc = (20/2^16)/sqrt(12*fs)*ones(length(freqs), 1);
% ASD of the measured angle by the QD in [rad/sqrt(Hz)]
asd_theta = asd_uc.*abs(squeeze(freqresp(G(1,1), freqs, 'Hz')));
figure;
hold on;
plot(f, sqrt(psd_Vph), 'DisplayName', '$\Gamma_{Vp_h}$');
plot(f, sqrt(psd_Vpv), 'DisplayName', '$\Gamma_{Vp_v}$');
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD $\left[\frac{V}{\sqrt{Hz}}\right]$')
legend('Location', 'southwest');
xlim([1, 1000]);
loglog(freqs, asd_theta)
% Then the corresponding ASD of the measured displacement by the interferometer is:
asd_L = asd_theta*10^(-6); % [m/sqrt(Hz)]
% And we integrate that to have the RMS value:
cps_L = 1/pi*cumtrapz(2*pi*freqs, (asd_L).^2);
% The RMS value is:
sqrt(cps_L(end))
% #+RESULTS:
% : 1.647e-11
figure;
hold on;
plot(t, Vch, 'DisplayName', '$Vc_h$');
plot(t, Vcv, 'DisplayName', '$Vc_v$');
hold off;
xlabel('Time [s]');
ylabel('Amplitude [V]');
xlim([t(1), t(end)]);
legend();
loglog(freqs, cps_L)
% We compute the Power Spectral Density of the voltage across the inductance used for horizontal and vertical positioning of the Cercalo.
% Let's estimate the beam angle error corresponding to 1 LSB of the sercalo's DAC.
% Gain of the Sercalo is approximatively 5 degrees for 10V.
% However the beam angle deviation is 4 times the angle deviation of the sercalo mirror, thus:
[psd_Vch, f] = pwelch(Vch, hanning(ceil(1*fs)), [], [], fs);
[psd_Vcv, ~] = pwelch(Vcv, hanning(ceil(1*fs)), [], [], fs);
d_alpha = 4*(20/2^16)*(5*pi/180)/10 % [rad]
figure;
hold on;
plot(f, sqrt(psd_Vch), 'DisplayName', '$\Gamma_{Vc_h}$');
plot(f, sqrt(psd_Vcv), 'DisplayName', '$\Gamma_{Vc_v}$');
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD $\left[\frac{V}{\sqrt{Hz}}\right]$')
legend('Location', 'southwest');
xlim([1, 1000]);
% #+RESULTS:
% : 1.0653e-05
% This corresponds to a measurement error of the Attocube equals to (in [m])
1e-6*d_alpha % [m]
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
% Coprime Factorization
load('mat/plant.mat', 'sys', 'Gi', 'Zc', 'Ga', 'Gc', 'Gn', 'Gd');
[fact, Ml, Nl] = lncf(Gc*Gi);
%% Clear Workspace and Close figures
clear; close all; clc;
@@ -139,3 +226,379 @@ end
% Rotation Matrix
G0 = freqresp(G, 0);
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
freqs = logspace(0, 2, 1000);
% Load Plant
load('mat/plant.mat', 'Gn', 'Gd');
% Analysis
% The plant is basically a constant until frequencies up to the required bandwidth.
% We get that constant value.
Gn0 = freqresp(inv(Gd)*Gn, 0);
% We design two controller containing 2 integrators and one lead near the crossover frequency set to 10Hz.
h = 2;
w0 = 2*pi*10;
Knh = 1/Gn0(1,1) * (w0/s)^2 * (1 + s/w0*h)/(1 + s/w0/h)/h;
Knv = 1/Gn0(2,2) * (w0/s)^2 * (1 + s/w0*h)/(1 + s/w0/h)/h;
figure;
hold on;
plot(freqs, abs(squeeze(freqresp(Gn0(1,1)*Knh, freqs, 'Hz'))))
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]'); ylabel('Loop Gain');
% Save
Kn = blkdiag(Knh, Knv);
Knd = c2d(Kn, 1e-4, 'tustin');
% The controllers can be downloaded [[./mat/K_newport.mat][here]].
save('mat/K_newport.mat', 'Kn', 'Knd');
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
fs = 1e4;
% Data Load and pre-processing
uh = load('mat/data_rep_h.mat', ...
't', 'Uch', 'Ucv', ...
'Unh', 'Unv', ...
'Vph', 'Vpv', ...
'Vnh', 'Vnv', ...
'Va');
uv = load('mat/data_rep_v.mat', ...
't', 'Uch', 'Ucv', ...
'Unh', 'Unv', ...
'Vph', 'Vpv', ...
'Vnh', 'Vnv', ...
'Va');
% Let's start one second after the first command in the system
i0 = find(uh.Unh ~= 0, 1) + fs;
iend = i0+fs*floor((length(uh.t)-i0)/fs);
uh.Uch([1:i0-1, iend:end]) = [];
uh.Ucv([1:i0-1, iend:end]) = [];
uh.Unh([1:i0-1, iend:end]) = [];
uh.Unv([1:i0-1, iend:end]) = [];
uh.Vph([1:i0-1, iend:end]) = [];
uh.Vpv([1:i0-1, iend:end]) = [];
uh.Vnh([1:i0-1, iend:end]) = [];
uh.Vnv([1:i0-1, iend:end]) = [];
uh.Va ([1:i0-1, iend:end]) = [];
uh.t ([1:i0-1, iend:end]) = [];
% We reset the time t
uh.t = uh.t - uh.t(1);
% Let's start one second after the first command in the system
i0 = find(uv.Unv ~= 0, 1) + fs;
iend = i0+fs*floor((length(uv.t)-i0)/fs);
uv.Uch([1:i0-1, iend:end]) = [];
uv.Ucv([1:i0-1, iend:end]) = [];
uv.Unh([1:i0-1, iend:end]) = [];
uv.Unv([1:i0-1, iend:end]) = [];
uv.Vph([1:i0-1, iend:end]) = [];
uv.Vpv([1:i0-1, iend:end]) = [];
uv.Vnh([1:i0-1, iend:end]) = [];
uv.Vnv([1:i0-1, iend:end]) = [];
uv.Va ([1:i0-1, iend:end]) = [];
uv.t ([1:i0-1, iend:end]) = [];
% We reset the time t
uv.t = uv.t - uv.t(1);
% Some Time domain plots
tend = 5; % [s]
figure;
ax1 = subplot(2, 2, 1);
hold on;
plot(uh.t(1:tend*fs), uh.Unh(1:tend*fs));
hold off;
xlabel('Time [s]'); ylabel('Voltage [V]');
title('Newport Tilt - Horizontal Direction');
ax3 = subplot(2, 2, 3);
hold on;
plot(uh.t(1:tend*fs), 1e9*uh.Va(1:tend*fs));
hold off;
xlabel('Time [s]'); ylabel('Distance [nm]');
title('Attocube - Horizontal Direction');
ax2 = subplot(2, 2, 2);
hold on;
plot(uv.t(1:tend*fs), uv.Unv(1:tend*fs));
hold off;
xlabel('Time [s]'); ylabel('Voltage [V]');
title('Newport Tilt - Vertical Direction');
ax4 = subplot(2, 2, 4);
hold on;
plot(uv.t(1:tend*fs), 1e9*uv.Va(1:tend*fs));
hold off;
xlabel('Time [s]'); ylabel('Distance [nm]');
title('Attocube - Vertical Direction');
linkaxes([ax1,ax2,ax3,ax4],'x');
linkaxes([ax1,ax2],'xy');
linkaxes([ax3,ax4],'xy');
% Verify Tracking Angle Error
% Let's verify that the positioning error of the beam is small and what could be the effect on the distance measured by the intereferometer.
load('./mat/plant.mat', 'Gd');
figure;
ax1 = subplot(1, 2, 1);
hold on;
plot(uh.t(1:2*fs), 1e6*uh.Vph(1:2*fs)/freqresp(Gd(1,1), 0), 'DisplayName', '$\theta_{h}$');
plot(uh.t(1:2*fs), 1e6*uh.Vpv(1:2*fs)/freqresp(Gd(2,2), 0), 'DisplayName', '$\theta_{v}$');
hold off;
xlabel('Time [s]'); ylabel('$\theta$ [$\mu$ rad]');
title('Newport Tilt - Horizontal Direction');
legend();
ax2 = subplot(1, 2, 2);
hold on;
plot(uv.t(1:2*fs), 1e6*uv.Vph(1:2*fs)/freqresp(Gd(1,1), 0), 'DisplayName', '$\theta_{h}$');
plot(uv.t(1:2*fs), 1e6*uv.Vpv(1:2*fs)/freqresp(Gd(2,2), 0), 'DisplayName', '$\theta_{v}$');
hold off;
xlabel('Time [s]'); ylabel('$\theta$ [$\mu$ rad]');
title('Newport Tilt - Vertical Direction');
legend();
linkaxes([ax1,ax2],'xy');
% #+NAME: fig:repeat_tracking_errors
% #+CAPTION: Tracking errors during the repeatability measurement ([[./figs/repeat_tracking_errors.png][png]], [[./figs/repeat_tracking_errors.pdf][pdf]])
% [[file:figs/repeat_tracking_errors.png]]
% Let's compute the PSD of the error to see the frequency content.
[psd_UhRh, f] = pwelch(uh.Vph/freqresp(Gd(1,1), 0), hanning(ceil(1*fs)), [], [], fs);
[psd_UhRv, ~] = pwelch(uh.Vpv/freqresp(Gd(2,2), 0), hanning(ceil(1*fs)), [], [], fs);
[psd_UvRh, ~] = pwelch(uv.Vph/freqresp(Gd(1,1), 0), hanning(ceil(1*fs)), [], [], fs);
[psd_UvRv, ~] = pwelch(uv.Vpv/freqresp(Gd(2,2), 0), hanning(ceil(1*fs)), [], [], fs);
figure;
ax1 = subplot(1, 2, 1);
hold on;
plot(f, sqrt(psd_UhRh), 'DisplayName', '$\Gamma_{\theta_h}$');
plot(f, sqrt(psd_UhRv), 'DisplayName', '$\Gamma_{\theta_v}$');
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD $\left[\frac{rad}{\sqrt{Hz}}\right]$')
legend('Location', 'southwest');
title('Newport Tilt - Horizontal Direction');
ax2 = subplot(1, 2, 2);
hold on;
plot(f, sqrt(psd_UvRh), 'DisplayName', '$\Gamma_{\theta_h}$');
plot(f, sqrt(psd_UvRv), 'DisplayName', '$\Gamma_{\theta_v}$');
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD $\left[\frac{rad}{\sqrt{Hz}}\right]$')
legend('Location', 'southwest');
title('Newport Tilt - Vertical Direction');
linkaxes([ax1,ax2],'xy');
xlim([1, 1000]);
% #+NAME: fig:psd_tracking_error_rad
% #+CAPTION: Power Spectral Density of the tracking errors ([[./figs/psd_tracking_error_rad.png][png]], [[./figs/psd_tracking_error_rad.pdf][pdf]])
% [[file:figs/psd_tracking_error_rad.png]]
% Let's convert that to errors in distance
% \[ \Delta L = L^\prime - L = \frac{L}{\cos(\alpha)} - L \approx \frac{L \alpha^2}{2} \]
% with
% - $L$ is the nominal distance traveled by the beam
% - $L^\prime$ is the distance traveled by the beam with an angle error
% - $\alpha$ is the angle error
L = 0.1; % [m]
[psd_UhLh, f] = pwelch(0.5*L*(uh.Vph/freqresp(Gd(1,1), 0)).^2, hanning(ceil(1*fs)), [], [], fs);
[psd_UhLv, ~] = pwelch(0.5*L*(uh.Vpv/freqresp(Gd(2,2), 0)).^2, hanning(ceil(1*fs)), [], [], fs);
[psd_UvLh, ~] = pwelch(0.5*L*(uv.Vph/freqresp(Gd(1,1), 0)).^2, hanning(ceil(1*fs)), [], [], fs);
[psd_UvLv, ~] = pwelch(0.5*L*(uv.Vpv/freqresp(Gd(2,2), 0)).^2, hanning(ceil(1*fs)), [], [], fs);
% Now, compare that with the PSD of the measured distance by the interferometer (Fig. [[fig:compare_tracking_error_attocube_meas]]).
[psd_Lh, f] = pwelch(uh.Va, hanning(ceil(1*fs)), [], [], fs);
[psd_Lv, ~] = pwelch(uv.Va, hanning(ceil(1*fs)), [], [], fs);
figure;
ax1 = subplot(1, 2, 1);
hold on;
plot(f, sqrt(psd_UhLh), 'DisplayName', '$\Gamma_{L_h}$');
plot(f, sqrt(psd_UhLv), 'DisplayName', '$\Gamma_{L_v}$');
plot(f, sqrt(psd_Lh), '--k', 'DisplayName', '$\Gamma_{L_h}$');
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD $\left[\frac{m}{\sqrt{Hz}}\right]$')
legend('Location', 'southwest');
title('Newport Tilt - Horizontal Direction');
ax2 = subplot(1, 2, 2);
hold on;
plot(f, sqrt(psd_UvLh), 'DisplayName', '$\Gamma_{L_h}$');
plot(f, sqrt(psd_UvLv), 'DisplayName', '$\Gamma_{L_v}$');
plot(f, sqrt(psd_Lv), '--k', 'DisplayName', '$\Gamma_{L_h}$');
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD $\left[\frac{m}{\sqrt{Hz}}\right]$')
legend('Location', 'southwest');
title('Newport Tilt - Vertical Direction');
linkaxes([ax1,ax2],'xy');
xlim([1, 1000]);
% Processing
% First, we get the mean value as measured by the interferometer for each value of the Newport angle.
Vahm = mean(reshape(uh.Va, [fs floor(length(uh.t)/fs)]),2);
Unhm = mean(reshape(uh.Unh, [fs floor(length(uh.t)/fs)]),2);
Vavm = mean(reshape(uv.Va, [fs floor(length(uv.t)/fs)]),2);
Unvm = mean(reshape(uv.Unv, [fs floor(length(uv.t)/fs)]),2);
% #+RESULTS:
% | Va - Horizontal [nm rms] | Va - Vertical [nm rms] |
% |--------------------------+------------------------|
% | 19.6 | 13.9 |
figure;
ax1 = subplot(1, 2, 1);
hold on;
plot(uh.Unh, uh.Va);
plot(Unhm, Vahm)
hold off;
xlabel('$V_{n,h}$ [V]'); ylabel('$V_a$ [m]');
ax2 = subplot(1, 2, 2);
hold on;
plot(uv.Unv, uv.Va);
plot(Unvm, Vavm)
hold off;
xlabel('$V_{n,v}$ [V]'); ylabel('$V_a$ [m]');
linkaxes([ax1,ax2],'xy');
% #+NAME: fig:repeat_plot_raw
% #+CAPTION: Repeatability of the measurement ([[./figs/repeat_plot_raw.png][png]], [[./figs/repeat_plot_raw.pdf][pdf]])
% [[file:figs/repeat_plot_raw.png]]
figure;
ax1 = subplot(1, 2, 1);
hold on;
plot(uh.Unh, 1e9*(uh.Va - repmat(Vahm, length(uh.t)/length(Vahm),1)));
hold off;
xlabel('$V_{n,h}$ [V]'); ylabel('$V_a$ [nm]');
ax2 = subplot(1, 2, 2);
hold on;
plot(uv.Unv, 1e9*(uv.Va - repmat(Vavm, length(uv.t)/length(Vavm),1)));
hold off;
xlabel('$V_{n,v}$ [V]'); ylabel('$V_a$ [nm]');
linkaxes([ax1,ax2],'xy');
ylim([-100 100]);
% Analysis of the non-repeatable contributions
% Let's know try to determine where does the non-repeatability comes from.
% From the 4QD signal, we can compute the angle error of the beam and thus determine the corresponding displacement measured by the attocube.
% We then take the non-repeatable part of this displacement and we compare that with the total non-repeatability.
% We also plot the displacement measured during the huddle test.
% All the signals are shown on Fig. [[]].
Vphm = mean(reshape(uh.Vph/freqresp(Gd(1,1), 0), [fs floor(length(uh.t)/fs)]),2);
Unhm = mean(reshape(uh.Unh, [fs floor(length(uh.t)/fs)]),2);
Vpvm = mean(reshape(uv.Vpv/freqresp(Gd(2,2), 0), [fs floor(length(uv.t)/fs)]),2);
Unvm = mean(reshape(uv.Unv, [fs floor(length(uv.t)/fs)]),2);
% =Vaheq= is the equivalent measurement error in [m] due to error angle of the Sercalo.
Vaheq = uh.Vph/freqresp(Gd(1,1), 0) - repmat(Vphm, length(uh.t)/length(Vphm),1);
Vaveq = uv.Vpv/freqresp(Gd(2,2), 0) - repmat(Vpvm, length(uv.t)/length(Vpvm),1);
Vaheq = sign(Vaheq).*Vaheq.^2;
Vaveq = sign(Vaveq).*Vaveq.^2;
ht = load('./mat/data_huddle_test_3.mat', 't', 'Va');
htm = 1e9*ht.Va(1:length(Vaheq)) - repmat(mean(1e9*ht.Va(1:length(Vaheq))), length(uh.t)/length(Vaheq),1);
figure;
ax1 = subplot(1, 2, 1);
hold on;
plot(uh.Unh, 1e9*(uh.Va - repmat(Vahm, length(uh.t)/length(Vahm),1)));
plot(uh.Unh, 1e9*ht.Va(1:length(Vaheq))-mean(1e9*ht.Va(1:length(Vaheq))));
plot(uh.Unh, 1e9*Vaheq);
hold off;
xlabel('$V_{n,h}$ [V]'); ylabel('$V_a$ [nm]');
ax2 = subplot(1, 2, 2);
hold on;
plot(uv.Unv, 1e9*(uv.Va - repmat(Vavm, length(uv.t)/length(Vavm),1)), 'DisplayName', 'Measured Non-Repeatability');
plot(uv.Unv, 1e9*ht.Va(1:length(Vaveq))-mean(1e9*ht.Va(1:length(Vaveq))), 'DisplayName', 'Huddle Test');
plot(uv.Unv, 1e9*Vaveq, 'DisplayName', 'Due to Sercalo Angle Error');
hold off;
xlabel('$V_{n,v}$ [V]'); ylabel('$V_a$ [nm]');
legend('location', 'northeast');
linkaxes([ax1,ax2],'xy');
ylim([-100 100]);

180
matlab/huddle_test.m Normal file
View File

@@ -0,0 +1,180 @@
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
% Load Data
ht_1 = load('./mat/data_huddle_test_1.mat', 't', 'Vph', 'Vpv', 'Va');
ht_2 = load('./mat/data_huddle_test_2.mat', 't', 'Vph', 'Vpv', 'Va');
ht_3 = load('./mat/data_huddle_test_3.mat', 't', 'Uch', 'Ucv', 'Vph', 'Vpv', 'Va');
ht_4 = load('./mat/data_huddle_test_4.mat', 't', 'Vph', 'Vpv', 'Va');
% ht_5 = load('./mat/data_huddle_test_5.mat', 't', 'Uch', 'Ucv', 'Vph', 'Vpv', 'Va');
fs = 1e4;
% Pre-processing
t0 = 1; % [s]
tend = 100; % [s]
ht_s = {ht_1 ht_2 ht_3 ht_4}
for i = 1:length(ht_s)
ht_s{i}.Vph(ht_s{i}.t<t0) = [];
ht_s{i}.Vpv(ht_s{i}.t<t0) = [];
ht_s{i}.Va(ht_s{i}.t<t0) = [];
ht_s{i}.t(ht_s{i}.t<t0) = [];
ht_s{i}.t = ht_s{i}.t - ht_s{i}.t(1); % We start at t=0
ht_s{i}.Vph(tend*fs+1:end) = [];
ht_s{i}.Vpv(tend*fs+1:end) = [];
ht_s{i}.Va(tend*fs+1:end) = [];
ht_s{i}.t(tend*fs+1:end) = [];
ht_s{i}.Va = ht_s{i}.Va - mean(ht_s{i}.Va);
end
ht_1 = ht_s{1};
ht_2 = ht_s{2};
ht_3 = ht_s{3};
ht_4 = ht_s{4};
% Time domain plots
figure;
ax1 = subaxis(2, 2, 1)
hold on;
plot(ht_1.t, 1e9*ht_1.Va);
hold off;
ylabel('Displacement [nm]');
set(gca, 'XTickLabel',[]);
title('OL');
ax2 = subaxis(2, 2, 2)
hold on;
plot(ht_2.t, 1e9*ht_2.Va);
hold off;
set(gca, 'XTickLabel',[]);
set(gca, 'YTickLabel',[]);
title('OL + CU');
ax3 = subaxis(2, 2, 3)
hold on;
plot(ht_3.t, 1e9*ht_3.Va);
hold off;
xlabel('Time [s]');
ylabel('Displacement [nm]');
title('CL + CU');
ax4 = subaxis(2, 2, 4)
hold on;
plot(ht_4.t, 1e9*ht_4.Va);
hold off;
xlabel('Time [s]');
set(gca, 'YTickLabel',[]);
title('OL + CU + AL');
linkaxes([ax1 ax2 ax3 ax4], 'xy');
% #+NAME: fig:huddle_test_Va
% #+CAPTION: Measurement of the Attocube during Huddle Test ([[./figs/huddle_test_Va.png][png]], [[./figs/huddle_test_Va.pdf][pdf]])
% [[file:figs/huddle_test_Va.png]]
figure;
ax1 = subaxis(2, 2, 1)
hold on;
plot(ht_1.t, ht_1.Vph);
plot(ht_1.t, ht_1.Vpv);
hold off;
ylabel('Voltage [V]');
set(gca, 'XTickLabel',[]);
title('OL');
ax2 = subaxis(2, 2, 2)
hold on;
plot(ht_2.t, ht_2.Vph);
plot(ht_2.t, ht_2.Vpv);
hold off;
set(gca, 'XTickLabel',[]);
set(gca, 'YTickLabel',[]);
title('OL + CU');
ax3 = subaxis(2, 2, 3)
hold on;
plot(ht_3.t, ht_3.Vph);
plot(ht_3.t, ht_3.Vpv);
hold off;
xlabel('Time [s]');
ylabel('Voltage [V]');
title('CL + CU');
ax4 = subaxis(2, 2, 4)
hold on;
plot(ht_4.t, ht_4.Vph);
plot(ht_4.t, ht_4.Vpv);
hold off;
xlabel('Time [s]');
set(gca, 'YTickLabel',[]);
title('OL + CU + AL');
linkaxes([ax1 ax2 ax3 ax4], 'xy');
% Power Spectral Density
win = hanning(ceil(1*fs));
[psd_Va1, f] = pwelch(ht_1.Va, win, [], [], fs);
[psd_Va2, ~] = pwelch(ht_2.Va, win, [], [], fs);
[psd_Va3, ~] = pwelch(ht_3.Va, win, [], [], fs);
[psd_Va4, ~] = pwelch(ht_4.Va, win, [], [], fs);
[psd_Vph1, ~] = pwelch(ht_1.Vph, win, [], [], fs);
[psd_Vph2, ~] = pwelch(ht_2.Vph, win, [], [], fs);
[psd_Vph3, ~] = pwelch(ht_3.Vph, win, [], [], fs);
[psd_Vph4, ~] = pwelch(ht_4.Vph, win, [], [], fs);
[psd_Vpv1, ~] = pwelch(ht_1.Vpv, win, [], [], fs);
[psd_Vpv2, ~] = pwelch(ht_2.Vpv, win, [], [], fs);
[psd_Vpv3, ~] = pwelch(ht_3.Vpv, win, [], [], fs);
[psd_Vpv4, ~] = pwelch(ht_4.Vpv, win, [], [], fs);
figure;
hold on;
plot(f, sqrt(psd_Va1), 'DisplayName', 'OL');
plot(f, sqrt(psd_Va2), 'DisplayName', 'OL + CU');
plot(f, sqrt(psd_Va3), 'DisplayName', 'CL + CU');
plot(f, sqrt(psd_Va4), 'DisplayName', 'OL + CU + AL');
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]');
ylabel('ASD $\left[\frac{m}{\sqrt{Hz}}\right]$');
legend('location', 'northeast');
xlim([1, 1000]);
% #+NAME: fig:huddle_test_psd_va
% #+CAPTION: PSD of the Interferometer measurement during Huddle tests ([[./figs/huddle_test_psd_va.png][png]], [[./figs/huddle_test_psd_va.pdf][pdf]])
% [[file:figs/huddle_test_psd_va.png]]
figure;
hold on;
plot(f, sqrt(psd_Vph1), 'DisplayName', 'OL');
plot(f, sqrt(psd_Vph2), 'DisplayName', 'OL + CU');
plot(f, sqrt(psd_Vph3), 'DisplayName', 'CL + CU');
plot(f, sqrt(psd_Vph4), 'DisplayName', 'OL + CU + AL');
hold off;
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]');
ylabel('ASD $\left[\frac{1}{\sqrt{Hz}}\right]$');
legend('location', 'northeast');
xlim([1, 1000]);

View File

@@ -4,7 +4,7 @@ tg = slrt;
if tg.Connected == "Yes"
if tg.Status == "stopped"
%% Load the application
tg.load('test_cercalo');
tg.load('test_sercalo');
%% Run the application
tg.start;

View File

@@ -16,7 +16,7 @@ uv = load('mat/data_cal_pd_v.mat', 't', 'Vph', 'Vpv', 'Vnv');
% We remove the first seconds where the Cercalo is turned on.
% We remove the first seconds where the Sercalo is turned on.
t0 = 1;
@@ -220,7 +220,7 @@ uv = load('mat/data_ucv.mat', 't', 'Ucv', 'Vcv');
% We remove the first seconds where the Cercalo is turned on.
% We remove the first seconds where the Sercalo is turned on.
t0 = 1;
@@ -273,7 +273,7 @@ xlim([1, 2000]);
% #+CAPTION: Identified and Theoretical Transfer Function $G_a G_i$ ([[./figs/current_amplifier_comp_theory_id.png][png]], [[./figs/current_amplifier_comp_theory_id.pdf][pdf]])
% [[file:figs/current_amplifier_comp_theory_id.png]]
% There is a gain mismatch, that is probably due to bad identification of the inductance and resistance measurement of the cercalo inductors.
% There is a gain mismatch, that is probably due to bad identification of the inductance and resistance measurement of the sercalo inductors.
% Thus, we suppose $G_a$ is perfectly known (the gain and cut-off frequency of the voltage amplifier is very accurate) and that $G_i$ is also well determined as it mainly depends on the resistor used in the amplifier that is well measured.
Gi_resp_h = abs(GaZcGi_h)./squeeze(abs(freqresp(Ga(1,1)*Zc(1,1), f, 'Hz')));
@@ -325,7 +325,7 @@ uv = load('mat/data_ucv.mat', 't', 'Ucv', 'Vph', 'Vpv');
% We remove the first seconds where the Cercalo is turned on.
% We remove the first seconds where the Sercalo is turned on.
t0 = 1;
@@ -481,9 +481,9 @@ xlim([10, 1000]); ylim([1e-2, 1e3]);
% #+NAME: fig:frf_cercalo_gain
% #+CAPTION: Frequency Response Matrix ([[./figs/frf_cercalo_gain.png][png]], [[./figs/frf_cercalo_gain.pdf][pdf]])
% [[file:figs/frf_cercalo_gain.png]]
% #+NAME: fig:frf_sercalo_gain
% #+CAPTION: Frequency Response Matrix ([[./figs/frf_sercalo_gain.png][png]], [[./figs/frf_sercalo_gain.pdf][pdf]])
% [[file:figs/frf_sercalo_gain.png]]
figure;
@@ -588,7 +588,7 @@ weight_Ucv_Vpv(f>1000) = 0;
% The weights are shown in Fig. [[fig:weights_cercalo]].
% The weights are shown in Fig. [[fig:weights_sercalo]].
figure;
@@ -605,9 +605,9 @@ legend('location', 'northwest');
% #+NAME: fig:weights_cercalo
% #+CAPTION: Weights amplitude ([[./figs/weights_cercalo.png][png]], [[./figs/weights_cercalo.pdf][pdf]])
% [[file:figs/weights_cercalo.png]]
% #+NAME: fig:weights_sercalo
% #+CAPTION: Weights amplitude ([[./figs/weights_sercalo.png][png]], [[./figs/weights_sercalo.pdf][pdf]])
% [[file:figs/weights_sercalo.png]]
% When we set some options for =vfit3=.
@@ -762,7 +762,7 @@ uv = load('mat/data_unv.mat', 't', 'Unv', 'Vph', 'Vpv');
% We remove the first seconds where the Cercalo is turned on.
% We remove the first seconds where the Sercalo is turned on.
t0 = 3;
@@ -1034,6 +1034,36 @@ Gn = blkdiag(tf(mean(abs(tf_Unh_Vph(f>10 & f<100)))), tf(mean(abs(tf_Unv_Vpv(f>1
% - $G_n$
% - $G_d$
% We name the input and output of each transfer function:
Gi.InputName = {'Uch', 'Ucv'};
Gi.OutputName = {'Ich', 'Icv'};
Zc.InputName = {'Ich', 'Icv'};
Zc.OutputName = {'Vtch', 'Vtcv'};
Ga.InputName = {'Vtch', 'Vtcv'};
Ga.OutputName = {'Vch', 'Vcv'};
Gc.InputName = {'Ich', 'Icv'};
Gc.OutputName = {'Vpch', 'Vpcv'};
Gn.InputName = {'Unh', 'Unv'};
Gn.OutputName = {'Vpnh', 'Vpnv'};
Gd.InputName = {'Rh', 'Rv'};
Gd.OutputName = {'Vph', 'Vpv'};
Sh = sumblk('Vph = Vpch + Vpnh');
Sv = sumblk('Vpv = Vpcv + Vpnv');
inputs = {'Uch', 'Ucv', 'Unh', 'Unv'};
outputs = {'Vch', 'Vcv', 'Ich', 'Icv', 'Rh', 'Rv', 'Vph', 'Vpv'};
sys = connect(Gi, Zc, Ga, Gc, Gn, inv(Gd), Sh, Sv, inputs, outputs);
% The file =mat/plant.mat= is accessible [[./mat/plant.mat][here]].
save('mat/plant.mat', 'Gi', 'Zc', 'Ga', 'Gc', 'Gn', 'Gd');
save('mat/plant.mat', 'sys', 'Gi', 'Zc', 'Ga', 'Gc', 'Gn', 'Gd');