Tangle matlab files

This commit is contained in:
Thomas Dehaeze 2025-03-17 22:20:43 +01:00
parent bc42284363
commit cf88e8c77f
8 changed files with 912 additions and 11 deletions

View File

@ -0,0 +1,165 @@
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
%% Path for functions, data and scripts
addpath('./src/'); % Path for scripts
addpath('./mat/'); % Path for data
addpath('./STEPS/'); % Path for Simscape Model
addpath('./subsystems/'); % Path for Subsystems Simulink files
%% Linearization options
opts = linearizeOptions;
opts.SampleTime = 0;
%% Open Simscape Model
mdl = 'detail_instrumentation_nass'; % Name of the Simulink File
open(mdl); % Open Simscape Model
%% Colors for the figures
colors = colororder;
freqs = logspace(1,4,1000); % Frequency vector [Hz]
% Closed-Loop Sensitivity to Instrumentation Disturbances
% <<ssec:detail_instrumentation_cl_sensitivity>>
% Several key noise sources are considered in the analysis (Figure ref:fig:detail_instrumentation_plant).
% These include the output voltage noise of the DAC ($n_{da}$), the output voltage noise of the voltage amplifier ($n_{amp}$), and the voltage noise of the ADC measuring the force sensor stacks ($n_{ad}$).
% Encoder noise, which is only used to estimate $R_z$, has been found to have minimal impact on the vertical sample error and is therefore omitted from this analysis for clarity.
% The transfer functions from these three noise sources (for one strut) to the vertical error of the sample are estimated from the multi-body model, which includes the APA300ML and the designed flexible joints (Figure ref:fig:detail_instrumentation_noise_sensitivities).
%% Identify the transfer functions from disturbance sources to vertical position error
% Let's initialize all the stages with default parameters.
initializeGround();
initializeGranite();
initializeTy();
initializeRy();
initializeRz();
initializeMicroHexapod();
initializeSample('m', 1);
initializeSimplifiedNanoHexapod();
initializeSimscapeConfiguration();
initializeDisturbances('enable', false);
initializeLoggingConfiguration('log', 'none');
initializeController('type', 'hac-iff');
initializeReferences();
% Decentralized IFF controller
wz = 2*pi*2;
xiz = 0.7;
Ghpf = (s^2/wz^2)/(s^2/wz^2 + 2*xiz*s/wz + 1);
Kiff = -200 * ... % Gain
1/(0.01*2*pi + s) * ... % LPF: provides integral action
Ghpf * ... % 2nd order HPF (limit low frequency gain)
eye(6); % Diagonal 6x6 controller (i.e. decentralized)
% Centralized HAC
wc = 2*pi*10; % Wanted crossover [rad/s]
H_int = wc/s; % Integrator
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a))); % Lead to increase phase margin
H_lpf = 1/(1 + s/2/pi/80); % Low Pass filter to increase robustness
Khac = -5e4 * ... % Gain
H_int * ... % Integrator
H_lead * ... % Low Pass filter
H_lpf * ... % Low Pass filter
eye(6); % 6x6 Diagonal
% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/dac_noise'], 1, 'input'); io_i = io_i + 1; % DAC noise [V]
io(io_i) = linio([mdl, '/amp_noise'], 1, 'input'); io_i = io_i + 1; % Voltage Amplifier noise [V]
io(io_i) = linio([mdl, '/NASS/adc_noise'], 1, 'input'); io_i = io_i + 1; % ADC noise [V]
io(io_i) = linio([mdl, '/NASS/enc_noise'], 1, 'input'); io_i = io_i + 1; % Encoder noise [m]
io(io_i) = linio([mdl, '/NASS'], 2, 'output', [], 'z'); io_i = io_i + 1; % Vertical error [m]
io(io_i) = linio([mdl, '/NASS'], 2, 'output', [], 'y'); io_i = io_i + 1; % Lateral error [m]
Gd = linearize(mdl, io);
Gd.InputName = {...
'nda1', 'nda2', 'nda3', 'nda4', 'nda5', 'nda6', ... % DAC and Voltage amplifier noise
'namp1', 'namp2', 'namp3', 'namp4', 'namp5', 'namp6', ... % DAC and Voltage amplifier noise
'nad1', 'nad2', 'nad3', 'nad4', 'nad5', 'nad6', ... % ADC noise
'ddL1', 'ddL2', 'ddL3', 'ddL4', 'ddL5', 'ddL6' ... % Encoder noise
};
Gd.OutputName = {'y', 'z'}; % Vertical error of the sample
%% Save Requirements
save('./mat/instrumentation_sensitivity.mat', 'Gd');
%% Transfer function from noise sources to vertical motion errors
freqs = logspace(0, 3, 1000);
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(Gd('z', 'nda1' ), freqs, 'Hz'))), 'DisplayName', '$\epsilon_z/n_{da}$');
plot(freqs, abs(squeeze(freqresp(Gd('z', 'namp1'), freqs, 'Hz'))), 'DisplayName', '$\epsilon_z/n_{amp}$');
plot(freqs, abs(squeeze(freqresp(Gd('z', 'nad1' ), freqs, 'Hz'))), 'DisplayName', '$\epsilon_z/n_{ad}$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Sensitivity [m/V]');
ylim([1e-9, 1e-4]);
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
xlim([1, 1e3]);
% Estimation of maximum instrumentation noise
% <<ssec:detail_instrumentation_max_noise_specs>>
% The most stringent requirement for the system is maintaining vertical vibrations below the smallest expected beam size of $100\,\text{nm}$, which corresponds to a maximum allowed vibration of $15\,\text{nm RMS}$.
% Several assumptions regarding the noise characteristics have been made.
% The DAC, ADC, and amplifier noise are considered uncorrelated, which is a reasonable assumption.
% Similarly, the noise sources corresponding to each strut are also assumed to be uncorrelated.
% This means that the power spectral densities (PSD) of the different noise sources are summed.
% The system symmetry has been utilized to further simplify the analysis.
% The effect of all struts on the vertical errors is identical, as verified from the extracted sensitivity curves.
% Therefore, only one strut is considered for this analysis, and the total effect of the six struts is calculated as six times the effect of one strut in terms of power, which translates to a factor of $\sqrt{6} \approx 2.5$ for RMS values.
% In order to derive specifications in terms of noise spectral density for each instrumentation component, a white noise profile is assumed, which is typical for these components.
% The noise specification is computed such that if all components operate at their maximum allowable noise levels, the specification for vertical error will still be met.
% While this represents a pessimistic approach, it provides a reasonable estimate of the required specifications.
% Based on this analysis, the obtained maximum noise levels are as follows: DAC maximum output noise ASD is established at $14\,\mu V/\sqrt{\text{Hz}}$, voltage amplifier maximum output voltage noise ASD at $280\,\mu V/\sqrt{\text{Hz}}$, and ADC maximum measurement noise ASD at $11\,\mu V/\sqrt{\text{Hz}}$.
% In terms of RMS noise, these translate to less than $1\,\text{mV RMS}$ for the DAC, less than $20\,\text{mV RMS}$ for the voltage amplifier, and less than $0.8\,\text{mV RMS}$ for the ADC.
% If the Amplitude Spectral Density of the noise of the ADC, DAC, and voltage amplifiers all remain below these specified maximum levels, then the induced vertical error will be maintained below 15nm RMS.
% Maximum wanted effect of each noise source on the vertical error
% Specifications: 15nm RMS
% divide by sqrt(6) because 6 struts
% divide by sqrt(3) because 3 considered noise sources
max_asd_z = 15e-9 / sqrt(6) / sqrt(3); % [m/sqrt(Hz)]
% Suppose unitary flat noise ASD => compute the effect on vertical noise
unit_asd = ones(1, length(freqs));
rms_unit_asd_dac = sqrt(sum((unit_asd.*abs(squeeze(freqresp(Gd('z', 'nda1' ), freqs, 'Hz'))).').^2));
rms_unit_asd_amp = sqrt(sum((unit_asd.*abs(squeeze(freqresp(Gd('z', 'namp1'), freqs, 'Hz'))).').^2));
rms_unit_asd_adc = sqrt(sum((unit_asd.*abs(squeeze(freqresp(Gd('z', 'nad1' ), freqs, 'Hz'))).').^2));
% Obtained maximum ASD for different instruments
max_dac_asd = max_asd_z./rms_unit_asd_dac; % [V/sqrt(Hz)]
max_amp_asd = max_asd_z./rms_unit_asd_amp; % [V/sqrt(Hz)]
max_adc_asd = max_asd_z./rms_unit_asd_adc; % [V/sqrt(Hz)]
% Estimation of the equivalent RMS noise
max_dac_rms = 1e3*max_dac_asd*sqrt(5e3) % [mV RMS]
max_amp_rms = 1e3*max_amp_asd*sqrt(5e3) % [mV RMS]
max_adc_rms = 1e3*max_adc_asd*sqrt(5e3) % [mV RMS]
%% Save Requirements
save('./mat/instrumentation_requirements.mat', ...
'max_dac_asd', 'max_amp_asd', 'max_adc_asd', ...
'max_dac_rms', 'max_amp_rms', 'max_adc_rms');

View File

@ -0,0 +1,117 @@
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
%% Path for functions, data and scripts
addpath('./src/'); % Path for scripts
addpath('./mat/'); % Path for data
addpath('./STEPS/'); % Path for Simscape Model
addpath('./subsystems/'); % Path for Subsystems Simulink files
%% Colors for the figures
colors = colororder;
freqs = logspace(1,4,1000); % Frequency vector [Hz]
%% Load computed requirements
load('instrumentation_requirements.mat')
% Small signal Bandwidth and Output Impedance
% Small signal bandwidth is particularly important for feedback applications as it can limit the overall bandwidth of the complete feedback system.
% A simplified electrical model of a voltage amplifier connected to a piezoelectric stack is shown in Figure ref:fig:detail_instrumentation_amp_output_impedance.
% This model is valid for small signals and provides insight into the small signal bandwidth limitation [[cite:&fleming14_desig_model_contr_nanop_system, chap. 14]].
% In this model, $R_o$ represents the output impedance of the amplifier.
% When combined with the piezoelectric load (represented as a capacitance $C_p$), it forms a first order low pass filter described by eqref:eq:detail_instrumentation_amp_output_impedance.
% \begin{equation}\label{eq:detail_instrumentation_amp_output_impedance}
% \frac{V_a}{V_i}(s) = \frac{1}{1 + \frac{s}{\omega_0}}, \quad \omega_0 = \frac{1}{R_o C_p}
% \end{equation}
% #+name: fig:detail_instrumentation_amp_output_impedance
% #+caption: Electrical model of a voltage amplifier with output impedance $R_0$ connected to a piezoelectric stack with capacitance $C_p$
% [[file:figs/detail_instrumentation_amp_output_impedance.png]]
% Consequently, the small signal bandwidth depends on the load capacitance and decreases as the load capacitance increases.
% For the APA300ML, the capacitive load of the two piezoelectric stacks corresponds to $C_p = 8.8\,\mu F$.
% If a small signal bandwidth of $f_0 = \frac{\omega_0}{2\pi} = 5\,\text{kHz}$ is desired, the voltage amplifier output impedance should be less than $R_0 = 3.6\,\Omega$.
Cp = 8.8e-6; % Capacitive load of the two piezoelectric actuators
f0 = 5e3; % Wanted low signal bandwidth [Hz]
Ro_max = 1/(2*pi*f0 * Cp); % Maximum wanted output impedance [Ohm]
% Large signal Bandwidth
% Large signal bandwidth relates to the maximum output capabilities of the amplifier in terms of amplitude as a function of frequency.
% Since the primary function of the NASS is position stabilization rather than scanning, this specification is less critical than the small signal bandwidth.
% However, considering potential scanning capabilities, a worst-case scenario of a constant velocity scan (triangular reference signal) with a repetition rate of $f_r = 100\,\text{Hz}$ using the full voltage range of the piezoelectric actuator ($V_{pp} = 170\,V$) is considered.
% There are two limiting factors for large signal bandwidth that should be evaluated:
% 1. Slew rate, which should exceed $2 \cdot V_{pp} \cdot f_r = 34\,V/ms$.
% This requirement is typically easily met by commercial voltage amplifiers.
% 2. Current output capabilities: as the capacitive impedance decreases inversely with frequency, it can reach very low values at high frequencies.
% To achieve high voltage at high frequency, the amplifier must therefore provide substantial current.
% The maximum required current can be calculated as $I_{\text{max}} = 2 \cdot V_{pp} \cdot f \cdot C_p = 0.3\,A$.
% Therefore, ideally, a voltage amplifier capable of providing $0.3\,A$ of current would be interesting for scanning applications.
%% Slew-rate specifications - Triangular scan
Vpp = 170; % Full voltage scan [V]
f0 = 100; % Repetition rate of the triangular scan [Hz]
slew_rate = 1e-3*2*Vpp*f0 % Required slew rate [V/ms]
%% Maximum Output Current - Triangular scan
max_current = 2*Vpp*f0*Cp % [A]
% #+name: fig:detail_instrumentation_adc_quantization
% #+caption: Probability density function $p(e)$ of the ADC quantization error $e$
% #+RESULTS:
% [[file:figs/detail_instrumentation_adc_quantization.png]]
% The variance (or time-average power) of the quantization noise is expressed by eqref:eq:detail_instrumentation_quant_power.
% \begin{equation}\label{eq:detail_instrumentation_quant_power}
% P_q = \int_{-q/2}^{q/2} e^2 p(e) de = \frac{q^2}{12}
% \end{equation}
% To compute the power spectral density of the quantization noise, which is defined as the Fourier transform of the noise's autocorrelation function, it is assumed that noise samples are uncorrelated.
% Under this assumption, the autocorrelation function approximates a delta function in the time domain.
% Since the Fourier transform of a delta function equals one, the power spectral density becomes frequency-independent (white noise).
% By Parseval's theorem, the power spectral density of the quantization noise $\Phi_q$ can be linked to the ADC sampling frequency and quantization step size eqref:eq:detail_instrumentation_psd_quant_noise.
% \begin{equation}\label{eq:detail_instrumentation_psd_quant_noise}
% \int_{-F_s/2}^{F_s/2} \Phi_q(f) d f = \int_{-q/2}^{q/2} e^2 p(e) de \quad \Longrightarrow \quad \Phi_q = \frac{q^2}{12 F_s} = \frac{\left(\frac{\Delta V}{2^n}\right)^2}{12 F_s} \quad \text{in } \left[ \frac{V^2}{\text{Hz}} \right]
% \end{equation}
% From a specified noise amplitude spectral density $\Gamma_{\text{max}}$, the minimum number of bits required to keep quantization noise below $\Gamma_{\text{max}}$ is calculated using eqref:eq:detail_instrumentation_min_n.
% \begin{equation}\label{eq:detail_instrumentation_min_n}
% n = \text{log}_2 \left( \frac{\Delta V}{\sqrt{12 F_s} \cdot \Gamma_{\text{max}}} \right)
% \end{equation}
% With a sampling frequency $F_s = 10\,\text{kHz}$, an input range $\Delta V = 20\,V$ and a maximum allowed ASD $\Gamma_{\text{max}} = 11\,\mu V/\sqrt{Hz}$, the minimum number of bits is $n_{\text{min}} = 12.4$, which is readily achievable with commercial ADCs.
delta_V = 20; % +/-10 V
Fs = 10e3; % Sampling Frequency [Hz]
max_adc_asd = 11e-6; % V/sqrt(Hz)
min_n = log2(delta_V/(sqrt(12*Fs)*max_adc_asd))
%% Estimate quantization noise of the ADC
delta_V = 20; % +/-10 V
n = 16; % number of bits
Fs = 10e3; % [Hz]
q = delta_V/2^n; % Quantization in [V]
q_psd = q^2/12/Fs; % Quantization noise Power Spectral Density [V^2/Hz]
q_asd = sqrt(q_psd) % Quantization noise Amplitude Spectral Density [V/sqrt(Hz)]

View File

@ -0,0 +1,619 @@
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
%% Path for functions, data and scripts
addpath('./src/'); % Path for scripts
addpath('./mat/'); % Path for data
addpath('./STEPS/'); % Path for Simscape Model
addpath('./subsystems/'); % Path for Subsystems Simulink files
%% Colors for the figures
colors = colororder;
freqs = logspace(1,4,1000); % Frequency vector [Hz]
%% Load computed requirements
load('instrumentation_requirements.mat')
%% Sensitivity to disturbances
load('instrumentation_sensitivity.mat', 'Gd');
% Measured Noise
% The measurement of ADC noise was performed by short-circuiting its input with a $50\,\Omega$ resistor and recording the digital values at a sampling rate of $10\,\text{kHz}$.
% The amplitude spectral density of the recorded values was computed and is presented in Figure ref:fig:detail_instrumentation_adc_noise_measured.
% The ADC noise exhibits characteristics of white noise with an amplitude spectral density of $5.6\,\mu V/\sqrt{\text{Hz}}$ (equivalent to $0.4\,\text{mV RMS}$), which satisfies the established specifications.
% All ADC channels demonstrated similar performance, so only one channel's noise profile is shown.
% If necessary, oversampling can be applied to further reduce the noise cite:lab13_improv_adc.
% To gain $w$ additional bits of resolution, the oversampling frequency $f_{os}$ should be set to $f_{os} = 4^w \cdot F_s$.
% Given that the ADC can operate at 200kSPS while the real-time controller runs at 10kSPS, an oversampling factor of 16 can be employed to gain approximately two additional bits of resolution (reducing noise by a factor of 4).
% This approach is effective because the noise approximates white noise and its amplitude exceeds 1 LSB (0.3 mV) [[cite:hauser91_princ_overs_conver]].
%% ADC noise
adc = load("2023-08-23_15-42_io131_adc_noise.mat");
% Spectral Analysis parameters
Ts = 1e-4;
Nfft = floor(1/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
% Identification of the transfer function from Va to di
[pxx, f] = pwelch(detrend(adc.adc_1, 0), win, Noverlap, Nfft, 1/Ts);
adc.pxx = pxx;
adc.f = f;
% estimated mean ASD
sprintf('Mean ASD of the ADC: %.1f uV/sqrt(Hz)', 1e6*sqrt(mean(adc.pxx)))
sprintf('Specifications: %.1f uV/sqrt(Hz)', 1e6*max_adc_asd)
% estimated RMS
sprintf('RMS of the ADC: %.2f mV RMS', 1e3*rms(detrend(adc.adc_1,0)))
sprintf('RMS specifications: %.2f mV RMS', max_adc_rms)
% Estimate quantization noise of the IO318 ADC
delta_V = 20; % +/-10 V
n = 16; % number of bits
Fs = 10e3; % [Hz]
adc.q = delta_V/2^n; % Quantization in [V]
adc.q_psd = adc.q^2/12/Fs; % Quantization noise Power Spectral Density [V^2/Hz]
adc.q_asd = sqrt(adc.q_psd); % Quantization noise Amplitude Spectral Density [V/sqrt(Hz)]
%% Measured ADC noise (IO318)
figure;
hold on;
plot(adc.f, sqrt(adc.pxx), 'color', colors(3,:), 'DisplayName', '$\Gamma_{q_{ad}}$')
plot([adc.f(2), adc.f(end)], [max_adc_asd, max_adc_asd], '--', 'color', colors(3,:), 'DisplayName', 'Specs')
plot([adc.f(2), adc.f(end)], [adc.q_asd, adc.q_asd], 'k--', 'DisplayName', 'Quantization noise (16 bits, $\pm 10\,V$)')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD [V/$\sqrt{Hz}$]');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
ylim([1e-10, 4e-4]); xlim([1, 5e3]);
xticks([1e0, 1e1, 1e2, 1e3])
% Reading of piezoelectric force sensor
% To further validate the ADC's capability to effectively measure voltage generated by a piezoelectric stack, a test was conducted using the APA95ML.
% The setup is illustrated in Figure ref:fig:detail_instrumentation_force_sensor_adc_setup, where two stacks are used as actuators (connected in parallel) and one stack serves as a sensor.
% The voltage amplifier employed in this setup has a gain of 20.
% #+name: fig:detail_instrumentation_force_sensor_adc_setup
% #+caption: Schematic of the setup to validate the use of the ADC for reading the force sensor volage
% [[file:figs/detail_instrumentation_force_sensor_adc_setup.png]]
% Step signals with an amplitude of $1\,V$ were generated using the DAC, and the ADC signal was recorded.
% The excitation signal (steps) and the measured voltage across the sensor stack are displayed in Figure ref:fig:detail_instrumentation_step_response_force_sensor.
% Two notable observations were made: an offset voltage of $2.26\,V$ was present, and the measured voltage exhibited an exponential decay response to the step input.
% These phenomena can be explained by examining the electrical schematic shown in Figure ref:fig:detail_instrumentation_force_sensor_adc, where the ADC has an input impedance $R_i$ and an input bias current $i_n$.
% The input impedance $R_i$ of the ADC, in combination with the capacitance $C_p$ of the piezoelectric stack sensor, forms an RC circuit with a time constant $\tau = R_i C_p$.
% The charge generated by the piezoelectric effect across the stack's capacitance gradually discharges into the input resistor of the ADC.
% Consequently, the transfer function from the generated voltage $V_p$ to the measured voltage $V_{\text{ADC}}$ is a first-order high-pass filter with the time constant $\tau$.
% An exponential curve was fitted to the experimental data, yielding a time constant $\tau = 6.5\,s$.
% With the capacitance of the piezoelectric sensor stack being $C_p = 4.4\,\mu F$, the internal impedance of the Speedgoat ADC was calculated as $R_i = \tau/C_p = 1.5\,M\Omega$, which closely aligns with the specified value of $1\,M\Omega$ found in the datasheet.
%% Read force sensor voltage with the ADC
load('force_sensor_steps.mat', 't', 'encoder', 'u', 'v');
% Exponential fit to compute the time constant
% Fit function
f_exp = @(b,x) b(1).*exp(-b(2).*x) + b(3);
% Three steps are performed at the following time intervals:
t_s = [ 2.5, 23;
23.8, 35;
35.8, 50];
tau = zeros(size(t_s, 1),1); % Time constant [s]
V0 = zeros(size(t_s, 1),1); % Offset voltage [V]
a = zeros(size(t_s, 1),1); %
for t_i = 1:size(t_s, 1)
t_cur = t(t_s(t_i, 1) < t & t < t_s(t_i, 2));
t_cur = t_cur - t_cur(1);
y_cur = v(t_s(t_i, 1) < t & t < t_s(t_i, 2));
nrmrsd = @(b) norm(y_cur - f_exp(b,t_cur)); % Residual Norm Cost Function
B0 = [0.5, 0.15, 2.2]; % Choose Appropriate Initial Estimates
[B,rnrm] = fminsearch(nrmrsd, B0); % Estimate Parameters B
a(t_i) = B(1);
tau(t_i) = 1/B(2);
V0(t_i) = B(3);
end
% Data to show the exponential fit
t_fit_1 = linspace(t_s(1,1), t_s(1,2), 100);
y_fit_1 = f_exp([a(1),1/tau(1),V0(1)], t_fit_1-t_s(1,1));
t_fit_2 = linspace(t_s(2,1), t_s(2,2), 100);
y_fit_2 = f_exp([a(2),1/tau(2),V0(2)], t_fit_2-t_s(2,1));
t_fit_3 = linspace(t_s(3,1), t_s(3,2), 100);
y_fit_3 = f_exp([a(3),1/tau(3),V0(3)], t_fit_3-t_s(3,1));
% Speedgoat ADC input impedance
Cp = 4.4e-6; % [F]
Rin = abs(mean(tau))/Cp; % [Ohm]
% Estimated input bias current
in = mean(V0)/Rin; % [A]
% Resistor added in parallel to the force sensor
fc = 0.5; % Wanted corner frequency [Hz]
Ra = Rin/(2*pi*fc*Cp*Rin - 1); % [Ohm]
% New ADC offset voltage
V_offset = Ra*Rin/(Ra + Rin) * in; % [V]
%% Measured voltage accross the sensor stacks - Voltage steps are applied to the actuators
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
nexttile();
hold on;
plot(t, u, 'DisplayName', '$u$');
plot(t, v, 'DisplayName', '$V_s$');
plot(t_fit_1, y_fit_1, 'k--', 'DisplayName', 'fit');
plot(t_fit_2, y_fit_2, 'k--', 'HandleVisibility', 'off');
plot(t_fit_3, y_fit_3, 'k--', 'HandleVisibility', 'off');
hold off;
xlabel('Time [s]'); ylabel('Voltage [V]');
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
xlim([0, 20]);
% #+name: fig:detail_instrumentation_force_sensor
% #+caption: Electrical schematic of the ADC measuring the piezoelectric force sensor (\subref{fig:detail_instrumentation_force_sensor_adc}), adapted from cite:reza06_piezoel_trans_vibrat_contr_dampin. Measured voltage $V_s$ while step voltages are generated for the actuator stacks (\subref{fig:detail_instrumentation_step_response_force_sensor}).
% #+attr_latex: :options [htbp]
% #+begin_figure
% #+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_force_sensor_adc}Electrical Schematic}
% #+attr_latex: :options {0.61\textwidth}
% #+begin_subfigure
% #+attr_latex: :scale 1
% [[file:figs/detail_instrumentation_force_sensor_adc.png]]
% #+end_subfigure
% #+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_step_response_force_sensor}Measured Signals}
% #+attr_latex: :options {0.35\textwidth}
% #+begin_subfigure
% #+attr_latex: :width 0.95\linewidth
% [[file:figs/detail_instrumentation_step_response_force_sensor.png]]
% #+end_subfigure
% #+end_figure
% The constant voltage offset can be explained by the input bias current $i_n$ of the ADC, represented in Figure ref:fig:detail_instrumentation_force_sensor_adc.
% At DC, the impedance of the piezoelectric stack is much larger than the input impedance of the ADC, and therefore the input bias current $i_n$ passing through the internal resistance $R_i$ produces a constant voltage offset $V_{\text{off}} = R_i \cdot i_n$.
% The input bias current $i_n$ is estimated from $i_n = V_{\text{off}}/R_i = 1.5\mu A$.
% In order to reduce the input voltage offset and to increase the corner frequency of the high pass filter, a resistor $R_p$ can be added in parallel to the force sensor, as illustrated in Figure ref:fig:detail_instrumentation_force_sensor_adc_R.
% This modification produces two beneficial effects: a reduction of input voltage offset through the relationship $V_{\text{off}} = (R_p R_i)/(R_p + R_i) i_n$, and an increase in the high pass corner frequency $f_c$ according to the equations $\tau = 1/(2\pi f_c) = (R_i R_p)/(R_i + R_p) C_p$.
% To validate this approach, a resistor $R_p \approx 82\,k\Omega$ was added in parallel with the force sensor as shown in Figure ref:fig:detail_instrumentation_force_sensor_adc_R.
% After incorporating this resistor, the same step response tests were performed, with results displayed in Figure ref:fig:detail_instrumentation_step_response_force_sensor_R.
% The measurements confirmed the expected improvements, with a substantially reduced offset voltage ($V_{\text{off}} = 0.15\,V$) and a much faster time constant ($\tau = 0.45\,s$).
% These results validate both the model of the ADC and the effectiveness of the added parallel resistor as a solution.
%% Read force sensor voltage with the ADC with added 82.7kOhm resistor
load('force_sensor_steps_R_82k7.mat', 't', 'encoder', 'u', 'v');
% Step times
t_s = [1.9, 6;
8.5, 13;
15.5, 21;
22.6, 26;
30.0, 36;
37.5, 41;
46.2, 49.5]; % [s]
tau = zeros(size(t_s, 1),1); % Time constant [s]
V0 = zeros(size(t_s, 1),1); % Offset voltage [V]
a = zeros(size(t_s, 1),1); %
for t_i = 1:size(t_s, 1)
t_cur = t(t_s(t_i, 1) < t & t < t_s(t_i, 2));
t_cur = t_cur - t_cur(1);
y_cur = v(t_s(t_i, 1) < t & t < t_s(t_i, 2));
nrmrsd = @(b) norm(y_cur - f_exp(b,t_cur)); % Residual Norm Cost Function
B0 = [0.5, 0.1, 2.2]; % Choose Appropriate Initial Estimates
[B,rnrm] = fminsearch(nrmrsd, B0); % Estimate Parameters B
a(t_i) = B(1);
tau(t_i) = 1/B(2);
V0(t_i) = B(3);
end
% Data to show the exponential fit
t_fit_1 = linspace(t_s(1,1), t_s(1,2), 100);
y_fit_1 = f_exp([a(1),1/tau(1),V0(1)], t_fit_1-t_s(1,1));
t_fit_2 = linspace(t_s(2,1), t_s(2,2), 100);
y_fit_2 = f_exp([a(2),1/tau(2),V0(2)], t_fit_2-t_s(2,1));
t_fit_3 = linspace(t_s(3,1), t_s(3,2), 100);
y_fit_3 = f_exp([a(3),1/tau(3),V0(3)], t_fit_3-t_s(3,1));
%% Measured voltage accross the sensor stacks - Voltage steps are applied to the actuators
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
nexttile();
hold on;
plot(t, u, 'DisplayName', '$u$');
plot(t, v, 'DisplayName', '$V_s$');
plot(t_fit_1, y_fit_1, 'k--', 'DisplayName', 'fit');
plot(t_fit_2, y_fit_2, 'k--', 'HandleVisibility', 'off');
plot(t_fit_3, y_fit_3, 'k--', 'HandleVisibility', 'off');
hold off;
xlabel('Time [s]'); ylabel('Voltage [V]');
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
xlim([0, 20]);
%% Femto Input Voltage Noise
femto = load('noise_femto.mat', 't', 'Vout', 'notes'); % Load Data
% Compute the equivalent voltage at the input of the amplifier
femto.Vout = femto.Vout/femto.notes.pre_amp.gain;
femto.Vout = femto.Vout - mean(femto.Vout);
Ts = (femto.t(end) - femto.t(1))/(length(femto.t) - 1);
Nfft = floor(1/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
% Power Spectral Density
[pxx, f] = pwelch(detrend(femto.Vout, 0), win, Noverlap, Nfft, 1/Ts);
% Save the results inside the struct
femto.pxx = pxx(f<=5e3);
femto.f = f(f<=5e3);
%% Measured input voltage noise of the Femto voltage pre-amplifier
figure;
hold on;
plot(femto.f, sqrt(femto.pxx), 'color', colors(5,:), 'DisplayName', '$\Gamma_{n_a}$');
plot(adc.f, sqrt(adc.pxx)./femto.notes.pre_amp.gain, 'color', colors(3,:), 'DisplayName', '$\Gamma_{q_{ad}}/|G_a|$')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD [$V/\sqrt{Hz}$]');
legend('location', 'northeast');
xlim([1, 5e3]); ylim([2e-10, 1e-7]);
xticks([1e0, 1e1, 1e2, 1e3]);
yticks([1e-9, 1e-8]);
% #+name: fig:detail_instrumentation_dac_setup
% #+caption: Measurement of the DAC output voltage noise. A pre-amplifier with a gain of 1000 is used before measuring the signal with the ADC.
% #+RESULTS:
% [[file:figs/detail_instrumentation_dac_setup.png]]
%% DAC Output Voltage Noise
dac = load('mat/noise_dac.mat', 't', 'Vn', 'notes');
% Take input acount the gain of the pre-amplifier
dac.Vn = dac.Vn/dac.notes.pre_amp.gain;
dac.Vn = dac.Vn - mean(dac.Vn);
Ts = (dac.t(end) - dac.t(1))/(length(dac.t) - 1);
Nfft = floor(1/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
% Identification of the transfer function from Va to di
[pxx, f] = pwelch(dac.Vn, win, Noverlap, Nfft, 1/Ts);
dac.pxx = pxx(f<=5e3);
dac.f = f(f<=5e3);
% Estimated mean ASD
sprintf('Mean ASD of the DAC: %.1f uV/sqrt(Hz)', 1e6*sqrt(mean(dac.pxx)))
sprintf('Specifications: %.1f uV/sqrt(Hz)', 1e6*max_dac_asd)
% Estimated RMS
sprintf('RMS of the DAC: %.2f mV RMS', 1e3*rms(dac.Vn))
sprintf('RMS specifications: %.2f mV RMS', max_dac_rms)
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(femto.f, sqrt(femto.pxx), 'color', colors(5,:), 'DisplayName', '$\Gamma_{n_a}$');
plot(dac.f, sqrt(dac.pxx), 'color', colors(1,:), 'DisplayName', '$\Gamma_{n_{da}}$');
plot([dac.f(2), dac.f(end)], [max_dac_asd, max_dac_asd], '--', 'color', colors(1,:), 'DisplayName', 'DAC specs')
plot(adc.f, sqrt(adc.pxx)./dac.notes.pre_amp.gain, 'color', colors(3,:), 'DisplayName', '$\Gamma_{q_{ad}}/|G_a|$')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD [$V/\sqrt{Hz}$]');
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
xlim([1, 5e3]); ylim([2e-10, 4e-4]);
xticks([1e0, 1e1, 1e2, 1e3]);
% Delay from ADC to DAC
% To measure the transfer function from DAC to ADC and verify that the bandwidth and latency of both instruments is sufficient, a direct connection was established between the DAC output and the ADC input.
% A white noise signal was generated by the DAC, and the ADC response was recorded.
% The resulting frequency response function from the digital DAC signal to the digital ADC signal is presented in Figure ref:fig:detail_instrumentation_dac_adc_tf.
% The observed frequency response function corresponds to exactly one sample delay, which aligns with the specifications provided by the manufacturer.
%% Measure transfer function from DAC to ADC
data_dac_adc = load("2023-08-22_15-52_io131_dac_to_adc.mat");
% Frequency analysis parameters
Ts = 1e-4; % Sampling Time [s]
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
[G_dac_adc, f] = tfestimate(data_dac_adc.dac_1, data_dac_adc.adc_1, win, Noverlap, Nfft, 1/Ts);
%
G_delay = exp(-Ts*s);
%% Measure transfer function from DAC to ADC - It fits a pure "1-sample" delay
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_dac_adc), 'color', colors(2,:), 'DisplayName', 'Measurement');
plot(f, abs(squeeze(freqresp(G_delay, f, 'Hz'))), 'k--', 'DisplayName', 'Pure Delay');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-1, 1e1]);
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
ax2 = nexttile();
hold on;
plot(f, 180/pi*unwrap(angle(G_dac_adc)), 'color', colors(2,:));
plot(f, 180/pi*unwrap(angle(squeeze(freqresp(G_delay, f, 'Hz')))), 'k--', 'DisplayName', 'Pure Delay');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-200, 20])
linkaxes([ax1,ax2],'x');
xlim([1, 5e3]);
xticks([1e0, 1e1, 1e2, 1e3]);
% #+name: fig:detail_instrumentation_pd200_setup
% #+caption: Setup used to measured the output voltage noise of the PD200 voltage amplifier. A gain $G_a = 1000$ was used for the instrumentation amplifier.
% #+RESULTS:
% [[file:figs/detail_instrumentation_pd200_setup.png]]
% The Amplitude Spectral Density $\Gamma_{n}(\omega)$ of the signal measured by the ADC was computed.
% From this, the Amplitude Spectral Density of the output voltage noise of the PD200 amplifier $n_p$ was derived, accounting for the gain of the pre-amplifier according to eqref:eq:detail_instrumentation_amp_asd.
% \begin{equation}\label{eq:detail_instrumentation_amp_asd}
% \Gamma_{n_p}(\omega) = \frac{\Gamma_n(\omega)}{|G_p(j\omega) G_a(j\omega)|}
% \end{equation}
% The computed Amplitude Spectral Density of the PD200 output noise is presented in Figure ref:fig:detail_instrumentation_pd200_noise.
% Verification was performed to confirm that the measured noise was predominantly from the PD200, with negligible contributions from the pre-amplifier noise or ADC noise.
% The measurements from all six amplifiers are displayed in this figure.
% The noise spectrum of the PD200 amplifiers exhibits several sharp peaks.
% While the exact cause of these peaks is not fully understood, their amplitudes remain below the specified limits and should not adversely affect system performance.
%% PD200 Output Voltage Noise
% Load all the measurements
pd200 = {};
for i = 1:6
pd200(i) = {load(['mat/noise_PD200_' num2str(i) '_10uF.mat'], 't', 'Vout', 'notes')};
end
% Take into account the pre-amplifier gain
for i = 1:6
pd200{i}.Vout = pd200{i}.Vout/pd200{i}.notes.pre_amp.gain;
end
% Sampling time / frequency
Ts = (pd200{1}.t(end) - pd200{1}.t(1))/(length(pd200{1}.t) - 1);
% Compute the PSD of the measured noise
Nfft = floor(1/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
for i = 1:6
% Identification of the transfer function from Va to di
[pxx, f] = pwelch(pd200{i}.Vout, win, Noverlap, Nfft, 1/Ts);
pd200{i}.pxx = pxx(f<=5e3);
pd200{i}.f = f(f<=5e3);
end
% Estimated RMS
sprintf('RMS of the PD200: %.2f mV RMS', 1e3*rms(detrend(pd200{1}.Vout,0)))
sprintf('RMS specifications: %.2f mV RMS', max_amp_rms)
%% Measured output voltage noise of the PD200 amplifiers
figure;
hold on;
plot([1 Fs/2], [max_amp_asd, max_amp_asd], '--', 'color', colors(2,:), 'DisplayName', 'Specs')
plot(pd200{1}.f, sqrt(pd200{1}.pxx), 'color', [colors(2, :), 0.5], 'DisplayName', '$\Gamma_{n_p}$');
for i = 2:6
plot(pd200{i}.f, sqrt(pd200{i}.pxx), 'color', [colors(2, :), 0.5], 'HandleVisibility', 'off');
end
plot(femto.f, sqrt(femto.pxx), 'color', [colors(5, :)], 'DisplayName', '$\Gamma_{n_a}$');
plot(adc.f, sqrt(adc.pxx)./pd200{1}.notes.pre_amp.gain, 'color', colors(3,:), 'DisplayName', '$\Gamma_{q_{ad}}/|G_a|$')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD [$V/\sqrt{Hz}$]');
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
ylim([1e-10, 4e-4]); xlim([1, 5e3]);
xticks([1e0, 1e1, 1e2, 1e3])
% Small Signal Bandwidth
% The small signal dynamics of all six PD200 amplifiers were characterized through frequency response measurements.
% A logarithmic sweep sine excitation voltage was generated using the Speedgoat DAC with an amplitude of $0.1\,V$, spanning frequencies from $1\,\text{Hz}$ to $5\,\text{kHz}$.
% The output voltage of the PD200 amplifier was measured via the monitor voltage output of the amplifier, while the input voltage (generated by the DAC) was measured with a separate ADC channel of the Speedgoat system.
% This measurement approach eliminates the influence of ADC-DAC-related time delays in the results.
% All six amplifiers demonstrated consistent transfer function characteristics. The amplitude response remains constant across a wide frequency range, and the phase shift is limited to less than 1 degree up to 500Hz, well within the specified requirements.
% The identified dynamics shown in Figure ref:fig:detail_instrumentation_pd200_tf can be accurately modeled as either a first-order low-pass filter or as a simple constant gain.
%% Load all the measurements
pd200_tf = {};
for i = 1:6
pd200_tf(i) = {load(['tf_pd200_' num2str(i) '_10uF_small_signal.mat'], 't', 'Vin', 'Vout', 'notes')};
end
% Compute sampling Frequency
Ts = (pd200_tf{1}.t(end) - pd200_tf{1}.t(1))/(length(pd200_tf{1}.t)-1);
% Compute all the transfer functions
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
for i = 1:length(pd200_tf)
[tf_est, f] = tfestimate(pd200_tf{i}.Vin, 20*pd200_tf{i}.Vout, win, Noverlap, Nfft, 1/Ts);
pd200_tf{i}.tf = tf_est(f<=5e3);
pd200_tf{i}.f = f(f<=5e3);
end
% Amplified model
Gp = 20/(1 + s/2/pi/25e3);
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(pd200_tf{1}.f, abs(pd200_tf{1}.tf), '-', 'color', [colors(2,:), 0.5], 'linewidth', 2.5, 'DisplayName', 'Measurement')
plot(pd200_tf{1}.f, abs(squeeze(freqresp(Gp, pd200_tf{1}.f, 'Hz'))), '--', 'color', colors(2,:), 'DisplayName', '$1^{st}$ order LPF')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Magnitude [V/V]'); set(gca, 'XTickLabel',[]);
hold off;
ylim([1, 1e2]);
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
ax2 = nexttile;
hold on;
plot(pd200_tf{1}.f, 180/pi*unwrap(angle(pd200_tf{1}.tf)), '-', 'color', [colors(2,:), 0.5], 'linewidth', 2.5)
plot(pd200_tf{1}.f, 180/pi*unwrap(angle(squeeze(freqresp(Gp, pd200_tf{1}.f, 'Hz')))), '--', 'color', colors(2,:))
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:2:360);
ylim([-13, 1]);
linkaxes([ax1,ax2],'x');
xlim([1, 5e3]);
% Linear Encoders
% To measure the noise of the encoder, the head and ruler were rigidly fixed together to ensure that no actual motion would be detected.
% Under these conditions, any measured signal would correspond solely to the encoder noise.
% The measurement setup is shown in Figure ref:fig:detail_instrumentation_vionic_bench.
% To minimize environmental disturbances, the entire bench was covered with a plastic bubble sheet during measurements.
% The amplitude spectral density of the measured displacement (which represents the measurement noise) is presented in Figure ref:fig:detail_instrumentation_vionic_asd.
% The noise profile exhibits characteristics of white noise with an amplitude of approximately $1\,\text{nm RMS}$, which complies with the system requirements.
%% Load all the measurements
enc = {};
for i = 1:6
enc(i) = {load(['mat/noise_meas_100s_20kHz_' num2str(i) '.mat'], 't', 'x')};
end
% Compute sampling Frequency
Ts = (enc{1}.t(end) - enc{1}.t(1))/(length(enc{1}.t)-1);
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
for i = 1:length(enc)
[pxx, f] = pwelch(detrend(enc{i}.x, 0), win, Noverlap, Nfft, 1/Ts);
enc{i}.pxx = pxx(f<=5e3);
enc{i}.pxx(2) = enc{i}.pxx(3); % Remove first point which corresponds to drifts
enc{i}.f = f(f<=5e3);
end
%% Measured Amplitude Spectral Density of the encoder position noise
figure;
hold on;
plot(enc{1}.f, sqrt(enc{1}.pxx), 'color', colors(4,:));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD [$m/\sqrt{Hz}$]');
xlim([1, 5e3]); ylim([1e-12, 1e-8]);
% Noise budgeting from measured instrumentation noise
% After characterizing all instrumentation components individually, their combined effect on the sample's vibration was assessed using the multi-body model developed earlier.
% The vertical motion induced by the noise sources, specifically the ADC noise, DAC noise, and voltage amplifier noise, is presented in Figure ref:fig:detail_instrumentation_cl_noise_budget.
% The total motion induced by all noise sources combined is approximately $1.5\,\text{nm RMS}$, which remains well within the specified limit of $15\,\text{nm RMS}$.
% This confirms that the selected instrumentation, with its measured noise characteristics, is suitable for the intended application.
%% Estimate the resulting errors induced by noise of instruments
f = dac.f;
% Vertical direction
psd_z_dac = 6*(abs(squeeze(freqresp(Gd('z', 'nda1' ), f, 'Hz'))).^2).*dac.pxx;
psd_z_adc = 6*(abs(squeeze(freqresp(Gd('z', 'nad1' ), f, 'Hz'))).^2).*adc.pxx;
psd_z_amp = 6*(abs(squeeze(freqresp(Gd('z', 'namp1'), f, 'Hz'))).^2).*pd200{1}.pxx;
psd_z_enc = 6*(abs(squeeze(freqresp(Gd('z', 'ddL1' ), f, 'Hz'))).^2).*enc{1}.pxx;
psd_z_tot = psd_z_dac + psd_z_adc + psd_z_amp + psd_z_enc;
rms_z_dac = sqrt(trapz(f, psd_z_dac));
rms_z_adc = sqrt(trapz(f, psd_z_adc));
rms_z_amp = sqrt(trapz(f, psd_z_amp));
rms_z_enc = sqrt(trapz(f, psd_z_enc));
rms_z_tot = sqrt(trapz(f, psd_z_tot));
% Lateral direction
psd_y_dac = 6*(abs(squeeze(freqresp(Gd('y', 'nda1' ), f, 'Hz'))).^2).*dac.pxx;
psd_y_adc = 6*(abs(squeeze(freqresp(Gd('y', 'nad1' ), f, 'Hz'))).^2).*adc.pxx;
psd_y_amp = 6*(abs(squeeze(freqresp(Gd('y', 'namp1'), f, 'Hz'))).^2).*pd200{1}.pxx;
psd_y_enc = 6*(abs(squeeze(freqresp(Gd('y', 'ddL1' ), f, 'Hz'))).^2).*enc{1}.pxx;
psd_y_tot = psd_y_dac + psd_y_adc + psd_y_amp + psd_y_enc;
rms_y_tot = sqrt(trapz(f, psd_y_tot));
%% Closed-loop noise budgeting using measured noise of instrumentation
figure;
hold on;
plot(f, sqrt(psd_z_amp), 'color', [colors(2,:)], 'linewidth', 2.5, 'DisplayName', 'PD200');
plot(f, sqrt(psd_z_dac), 'color', [colors(1,:)], 'linewidth', 2.5, 'DisplayName', 'DAC')
plot(f, sqrt(psd_z_adc), 'color', [colors(3,:)], 'linewidth', 2.5, 'DisplayName', 'ADC')
plot(f, sqrt(psd_z_tot), 'k-', 'DisplayName', sprintf('Total: %.1f nm RMS', 1e9*rms_z_tot));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD [$m/\sqrt{Hz}$]');
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
xlim([1, 5e3]); ylim([1e-14, 1e-9]);
xticks([1e0, 1e1, 1e2, 1e3]);

Binary file not shown.

View File

@ -1754,23 +1754,23 @@ The identified dynamics shown in Figure ref:fig:detail_instrumentation_pd200_tf
#+begin_src matlab :exports none
%% Load all the measurements
pd200 = {};
pd200_tf = {};
for i = 1:6
pd200(i) = {load(['tf_pd200_' num2str(i) '_10uF_small_signal.mat'], 't', 'Vin', 'Vout', 'notes')};
pd200_tf(i) = {load(['tf_pd200_' num2str(i) '_10uF_small_signal.mat'], 't', 'Vin', 'Vout', 'notes')};
end
% Compute sampling Frequency
Ts = (pd200{1}.t(end) - pd200{1}.t(1))/(length(pd200{1}.t)-1);
Ts = (pd200_tf{1}.t(end) - pd200_tf{1}.t(1))/(length(pd200_tf{1}.t)-1);
% Compute all the transfer functions
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
for i = 1:length(pd200)
[tf_est, f] = tfestimate(pd200{i}.Vin, 20*pd200{i}.Vout, win, Noverlap, Nfft, 1/Ts);
pd200{i}.tf = tf_est(f<=5e3);
pd200{i}.f = f(f<=5e3);
for i = 1:length(pd200_tf)
[tf_est, f] = tfestimate(pd200_tf{i}.Vin, 20*pd200_tf{i}.Vout, win, Noverlap, Nfft, 1/Ts);
pd200_tf{i}.tf = tf_est(f<=5e3);
pd200_tf{i}.f = f(f<=5e3);
end
% Amplified model
@ -1783,8 +1783,8 @@ tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(pd200{1}.f, abs(pd200{1}.tf), '-', 'color', [colors(2,:), 0.5], 'linewidth', 2.5, 'DisplayName', 'Measurement')
plot(pd200{1}.f, abs(squeeze(freqresp(Gp, pd200{1}.f, 'Hz'))), '--', 'color', colors(2,:), 'DisplayName', '$1^{st}$ order LPF')
plot(pd200_tf{1}.f, abs(pd200_tf{1}.tf), '-', 'color', [colors(2,:), 0.5], 'linewidth', 2.5, 'DisplayName', 'Measurement')
plot(pd200_tf{1}.f, abs(squeeze(freqresp(Gp, pd200_tf{1}.f, 'Hz'))), '--', 'color', colors(2,:), 'DisplayName', '$1^{st}$ order LPF')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Magnitude [V/V]'); set(gca, 'XTickLabel',[]);
@ -1795,8 +1795,8 @@ leg.ItemTokenSize(1) = 15;
ax2 = nexttile;
hold on;
plot(pd200{1}.f, 180/pi*unwrap(angle(pd200{1}.tf)), '-', 'color', [colors(2,:), 0.5], 'linewidth', 2.5)
plot(pd200{1}.f, 180/pi*unwrap(angle(squeeze(freqresp(Gp, pd200{1}.f, 'Hz')))), '--', 'color', colors(2,:))
plot(pd200_tf{1}.f, 180/pi*unwrap(angle(pd200_tf{1}.tf)), '-', 'color', [colors(2,:), 0.5], 'linewidth', 2.5)
plot(pd200_tf{1}.f, 180/pi*unwrap(angle(squeeze(freqresp(Gp, pd200_tf{1}.f, 'Hz')))), '--', 'color', colors(2,:))
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;