O-band Amp Characterization Measurement
This commit is contained in:
@@ -12,6 +12,7 @@ classdef Exfo_laser < handle
|
||||
safety_mode
|
||||
end
|
||||
|
||||
%% Any user can call these methods to alter the laser state
|
||||
methods (Access=public)
|
||||
|
||||
function obj = Exfo_laser(options)
|
||||
@@ -169,6 +170,10 @@ classdef Exfo_laser < handle
|
||||
end
|
||||
|
||||
end %public mehtods
|
||||
|
||||
|
||||
% DAU must not be able to call these funcitons outside of the class!
|
||||
% Hence they are private! :-)
|
||||
methods (Access=private)
|
||||
|
||||
function v = connectLaser_(obj)
|
||||
@@ -180,7 +185,7 @@ classdef Exfo_laser < handle
|
||||
configureTerminator(v, "CR", "CR"); % Set the terminator to carriage return (CR)
|
||||
elseif obj.lab_interface == lab_interface.gpib
|
||||
% Connect via GPIB adress
|
||||
visastring = ['GPIB0::',obj.connection_id,'::INSTR'];
|
||||
visastring = ['GPIB1::',obj.connection_id,'::INSTR'];
|
||||
v = visadev(visastring);
|
||||
else
|
||||
error('Interface not supported');
|
||||
@@ -342,8 +347,13 @@ classdef Exfo_laser < handle
|
||||
command = ['CH', num2str(channel), ':L=', num2str(desiredWavelength_nm)];
|
||||
writeline(serialObj, command);
|
||||
|
||||
if abs(obj.cur_wavelength - desiredWavelength_nm)>30
|
||||
pause(5); %it takes time to tune the laser! If this is not here it WILL break!
|
||||
else
|
||||
pause(1.5);
|
||||
end
|
||||
if serialObj.Type == visalib.InterfaceType.serial
|
||||
pause(0.2);
|
||||
pause(1.5);
|
||||
response = readline(serialObj);
|
||||
success = contains(response, 'OK');
|
||||
end
|
||||
|
||||
83
Classes/05_Lab/LabDeviceTemplate.m
Normal file
83
Classes/05_Lab/LabDeviceTemplate.m
Normal file
@@ -0,0 +1,83 @@
|
||||
classdef LabDeviceTemplate < handle
|
||||
|
||||
% Minimal template for lab device control via VISA / SCPI
|
||||
% Copy this file and adapt:
|
||||
% - visaAddress
|
||||
% - public API methods
|
||||
% - private SCPI commands
|
||||
|
||||
properties (Access = public)
|
||||
active logical = true
|
||||
end
|
||||
|
||||
properties (Access = protected)
|
||||
visaAddress string
|
||||
end
|
||||
|
||||
methods (Access = public)
|
||||
|
||||
function obj = LabDeviceTemplate(options)
|
||||
arguments
|
||||
options.active logical = true
|
||||
options.visaAddress string = ""
|
||||
end
|
||||
|
||||
% apply options
|
||||
fn = fieldnames(options);
|
||||
for k = 1:numel(fn)
|
||||
obj.(fn{k}) = options.(fn{k});
|
||||
end
|
||||
|
||||
% optional: quick connectivity check
|
||||
if obj.active
|
||||
v = obj.connect();
|
||||
writeline(v,"*IDN?");
|
||||
idn = readline(v);
|
||||
disp("Connected to: " + string(idn));
|
||||
delete(v);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function success = set(obj, value)
|
||||
% Example public API
|
||||
v = obj.connect();
|
||||
success = obj.setValue_(v, value);
|
||||
delete(v);
|
||||
end
|
||||
|
||||
|
||||
function value = read(obj)
|
||||
% Example readback
|
||||
v = obj.connect();
|
||||
value = obj.readValue_(v);
|
||||
delete(v);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
methods (Access = protected)
|
||||
|
||||
function v = connect(obj)
|
||||
assert(obj.visaAddress ~= "", "No VISA address defined");
|
||||
v = visadev(obj.visaAddress);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
methods (Access = private)
|
||||
|
||||
function success = setValue_(~, v, value)
|
||||
writeline(v, sprintf("SET %g", value));
|
||||
success = true;
|
||||
end
|
||||
|
||||
function value = readValue_(~, v)
|
||||
writeline(v, "READ?");
|
||||
value = str2double(readline(v));
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
327
Classes/05_Lab/OSA_Advantest.m
Normal file
327
Classes/05_Lab/OSA_Advantest.m
Normal file
@@ -0,0 +1,327 @@
|
||||
classdef OSA_Advantest < handle
|
||||
% Advantest Q8384 Optical Spectrum Analyzer (GPIB)
|
||||
% User inputs in nanometers
|
||||
|
||||
% EXAMPLE:
|
||||
|
||||
% span_nm = 70;
|
||||
% lambda_c_nm = 1310;
|
||||
% osa = OSA_Advantest( ...
|
||||
% 'span_nm', span_nm, ...
|
||||
% 'lambda_c_nm', lambda_c_nm, ...
|
||||
% 'resolution_nm', 0.1, ...
|
||||
% 'sampling_points', 10001 );
|
||||
%
|
||||
% osa.configure();
|
||||
% [lambda_nm, psd_dBm] = osa.measure();
|
||||
% figure()
|
||||
% plot(lambda_nm,psd_dBm);
|
||||
|
||||
properties (Access = public)
|
||||
span_nm double = 5
|
||||
resolution_nm double = 0.05
|
||||
lambda_c_nm double = 1550
|
||||
sampling_points double = 1001
|
||||
timeout_s double = 10
|
||||
|
||||
lambda_nm
|
||||
psd_dBm
|
||||
end
|
||||
|
||||
properties (Constant)
|
||||
valid_resolutions_nm = [0.01 0.02 0.05 0.10 0.20 0.50]
|
||||
valid_sampling_points = [101 201 501 1001 2001 5001 10001];
|
||||
end
|
||||
|
||||
properties (Access = protected)
|
||||
visaAddress string = "GPIB0::3::INSTR"
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = OSA_Advantest(options)
|
||||
arguments
|
||||
options.visaAddress string = "GPIB0::3::INSTR"
|
||||
options.span_nm double = 5
|
||||
options.resolution_nm double = 0.05
|
||||
options.lambda_c_nm double = 1550
|
||||
options.sampling_points double = 1001
|
||||
options.timeout_s double = 10
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for k = 1:numel(fn)
|
||||
obj.(fn{k}) = options.(fn{k});
|
||||
end
|
||||
|
||||
assert(ismember(obj.resolution_nm, obj.valid_resolutions_nm), ...
|
||||
'Invalid resolution. Allowed: 0.01 | 0.02 | 0.05 | 0.10 | 0.20 | 0.50 nm');
|
||||
assert(ismember(obj.sampling_points, obj.valid_sampling_points), ...
|
||||
'Invalid # of Samp. Points. Allowed: 101 201 501 1001 2001 5001 10001');
|
||||
|
||||
v = obj.connect();
|
||||
writeline(v,"*IDN?");
|
||||
disp(readline(v));
|
||||
delete(v);
|
||||
end
|
||||
|
||||
function configure(obj)
|
||||
assert(ismember(obj.resolution_nm, obj.valid_resolutions_nm), ...
|
||||
'Invalid resolution. Allowed: 0.01 | 0.02 | 0.05 | 0.10 | 0.20 | 0.50 nm');
|
||||
assert(ismember(obj.sampling_points, obj.valid_sampling_points), ...
|
||||
'Invalid # of Samp. Points. Allowed: 101 201 501 1001 2001 5001 10001');
|
||||
|
||||
v = obj.connect();
|
||||
|
||||
writeline(v,"FMT0,HED0,SDL0");
|
||||
writeline(v,"BUZ0");
|
||||
writeline(v,"QUI0");
|
||||
|
||||
writeline(v, sprintf("RES %gnm", obj.resolution_nm));
|
||||
writeline(v, sprintf("SPT %g", obj.sampling_points));
|
||||
writeline(v, sprintf("CEN %gnm", obj.lambda_c_nm));
|
||||
writeline(v, sprintf("SPA %gnm", obj.span_nm));
|
||||
writeline(v,"SWE 0");
|
||||
|
||||
delete(v);
|
||||
end
|
||||
|
||||
function [osa_x_nm, osa_y_dBm] = measure(obj)
|
||||
v = obj.connect();
|
||||
|
||||
writeline(v,"CSB");
|
||||
writeline(v,"MEA1");
|
||||
|
||||
active = 1; cnt = 0;
|
||||
while active
|
||||
pause(1);
|
||||
writeline(v,"MEA?");
|
||||
active = str2double(readline(v));
|
||||
cnt = cnt + 1;
|
||||
if cnt > obj.timeout_s
|
||||
delete(v);
|
||||
error("OSA_Q8384:Timeout","Measurement timeout");
|
||||
end
|
||||
end
|
||||
|
||||
writeline(v,"PKL");
|
||||
|
||||
writeline(v,"ODN?");
|
||||
readline(v); % number of points (not strictly needed)
|
||||
|
||||
writeline(v,"FMT0,HED0,SDL0");
|
||||
|
||||
writeline(v,"OSD0"); % Y-axis
|
||||
osa_y_dBm = str2num(readline(v));
|
||||
|
||||
writeline(v,"OSD1"); % X-axis
|
||||
osa_x_m = str2num(readline(v));
|
||||
osa_x_nm = osa_x_m.*1e9;
|
||||
delete(v);
|
||||
|
||||
obj.lambda_nm=osa_x_nm;
|
||||
obj.psd_dBm=osa_y_dBm;
|
||||
|
||||
end
|
||||
|
||||
% --- Replace / extend plot() in OSA_Q8384 class ---
|
||||
|
||||
function plot(obj, options)
|
||||
arguments
|
||||
obj
|
||||
options.FigureNumber double = 1
|
||||
options.Hold logical = true
|
||||
options.Title string = "OSA Spectrum"
|
||||
options.LineWidth double = 0.8
|
||||
options.Grid logical = true
|
||||
options.Color (1,3) double = [0 0.4470 0.7410]
|
||||
options.DisplayName string = ""
|
||||
end
|
||||
|
||||
if isempty(obj.psd_dBm) || isempty(obj.lambda_nm)
|
||||
error('Record spectrum first: run osa.measure()');
|
||||
end
|
||||
|
||||
fig = figure(options.FigureNumber);
|
||||
fig.Color = 'w';
|
||||
|
||||
ax = gca;
|
||||
if ~options.Hold
|
||||
cla(ax);
|
||||
else
|
||||
hold(ax,'on');
|
||||
end
|
||||
|
||||
plot(ax, obj.lambda_nm, obj.psd_dBm, ...
|
||||
'LineWidth', options.LineWidth, ...
|
||||
'Color', options.Color, ...
|
||||
'DisplayName', options.DisplayName);
|
||||
|
||||
xlabel(ax,'Wavelength [nm]');
|
||||
ylabel(ax,'Optical Power [dBm]');
|
||||
|
||||
if options.Title ~= ""
|
||||
title(ax, options.Title);
|
||||
end
|
||||
|
||||
xlim(ax,[min(obj.lambda_nm) max(obj.lambda_nm)]);
|
||||
yl = ylim(ax);
|
||||
ylim(ax,[yl(1)-1 yl(2)+1]);
|
||||
|
||||
if options.Grid
|
||||
grid(ax,'on');
|
||||
grid(ax,'minor');
|
||||
end
|
||||
|
||||
set(ax, ...
|
||||
'FontSize',11, ...
|
||||
'LineWidth',1, ...
|
||||
'Box','on');
|
||||
|
||||
if options.DisplayName ~= ""
|
||||
legend(ax,'show','Location','best');
|
||||
end
|
||||
end
|
||||
|
||||
function osnr = computeOSNR(obj, options)
|
||||
% OSNR computation based on ratio method (RBW-independent)
|
||||
% Assumes obj.lambda_nm [nm], obj.psd_dBm [dBm/RBW] exist
|
||||
|
||||
arguments
|
||||
obj
|
||||
options.bw_signal_nm double = 0.1 % channel bandwidth (0.1 for CW?!?!)
|
||||
options.ref_bw_nm double = 0.1 % OSNR reference bandwidth
|
||||
options.carrier_frequency = NaN;
|
||||
options.noise_guard_nm = 1;
|
||||
options.noise_window_nm =1;
|
||||
options.debugplots = 0;
|
||||
end
|
||||
|
||||
assert(~isempty(obj.lambda_nm) && ~isempty(obj.psd_dBm), ...
|
||||
'Run measure() before computing OSNR');
|
||||
|
||||
obj.lambda_nm = obj.lambda_nm;
|
||||
psd = obj.psd_dBm;
|
||||
|
||||
% --- find carrier wavelength recorded in OSA ---
|
||||
% options.carrier_frequency [nm], options.noise_guard_nm, options.noise_window_nm
|
||||
|
||||
if ~isnan(options.carrier_frequency)
|
||||
|
||||
% local search window around expected CW carrier
|
||||
idx_search = obj.lambda_nm > (options.carrier_frequency - options.noise_guard_nm) & ...
|
||||
obj.lambda_nm < (options.carrier_frequency + options.noise_guard_nm);
|
||||
|
||||
[~, imax_local] = max(psd(idx_search));
|
||||
lambda_search = obj.lambda_nm(idx_search);
|
||||
lambda_c = lambda_search(imax_local);
|
||||
[~,imax] = find(obj.lambda_nm==lambda_c);
|
||||
|
||||
% define local noise windows directly adjacent to signal (guarded)
|
||||
idx_noise_left = obj.lambda_nm > (lambda_c - options.noise_guard_nm - options.noise_window_nm) & ...
|
||||
obj.lambda_nm < (lambda_c - options.noise_guard_nm);
|
||||
|
||||
idx_noise_right = obj.lambda_nm > (lambda_c + options.noise_guard_nm) & ...
|
||||
obj.lambda_nm < (lambda_c + options.noise_guard_nm + options.noise_window_nm);
|
||||
|
||||
idx_noise = idx_noise_left | idx_noise_right;
|
||||
|
||||
else
|
||||
% fallback: global maximum (unsafe for pathological ASE cases)
|
||||
[~, imax] = max(psd);
|
||||
lambda_c = obj.lambda_nm(imax);
|
||||
|
||||
% ASE from outer quarters of spectrum
|
||||
N = numel(obj.lambda_nm);
|
||||
idx_noise = false(size(obj.lambda_nm));
|
||||
idx_noise([3:round(N/4), round(3*N/4):N-1]) = true;
|
||||
end
|
||||
|
||||
% --- extract signal window ---
|
||||
idx_signal = obj.lambda_nm > (lambda_c - options.bw_signal_nm/2) & ...
|
||||
obj.lambda_nm < (lambda_c + options.bw_signal_nm/2);
|
||||
|
||||
if ~any(idx_signal)
|
||||
idx_signal(imax) = true;
|
||||
end
|
||||
|
||||
cc_lambda = obj.lambda_nm(idx_signal);
|
||||
cc_psd = psd(idx_signal);
|
||||
|
||||
% --- estimate ASE from local noise (flat ASE assumption) ---
|
||||
noise_lambda = obj.lambda_nm(idx_noise);
|
||||
noise_psd = psd(idx_noise);
|
||||
|
||||
|
||||
ase_psd = interp1(noise_lambda, noise_psd, cc_lambda, ...
|
||||
'linear', 'extrap');
|
||||
|
||||
% --- convert to linear (mW / RBW) ---
|
||||
sig_lin = 10.^(cc_psd/10);
|
||||
ase_lin = 10.^(ase_psd/10);
|
||||
|
||||
% --- integrate (RBW cancels) ---
|
||||
P_sig_lin = sum(sig_lin);
|
||||
P_ase_lin = sum(ase_lin);
|
||||
|
||||
P_sig_dB = 10*log10(P_sig_lin);
|
||||
P_ase_dB = 10*log10(P_ase_lin);
|
||||
|
||||
osnr.p_sig_db = P_sig_dB;
|
||||
osnr.p_ase_db = P_ase_dB;
|
||||
|
||||
% --- OSNR direct from peak value to the noise estimate out of band ---
|
||||
osnr.direct_dB = psd(imax) - 10*log10(mean(ase_lin)) ;
|
||||
|
||||
% --- OSNR in measurement bandwidth ---
|
||||
osnr.measured_dB = P_sig_dB - P_ase_dB;
|
||||
|
||||
% --- normalize to reference bandwidth (usually 0.1 nm) ---
|
||||
osnr.ref_dB = osnr.measured_dB + ...
|
||||
10*log10(options.bw_signal_nm / options.ref_bw_nm);
|
||||
|
||||
% --- ASE-corrected OSNR (optional definition) ---
|
||||
osnr.corrected_dB = ...
|
||||
10*log10(10^(P_sig_dB/10) - 10^(P_ase_dB/10)) - P_ase_dB;
|
||||
|
||||
osnr.corrected_ref_dB = osnr.corrected_dB + ...
|
||||
10*log10(options.bw_signal_nm / options.ref_bw_nm);
|
||||
|
||||
% metadata
|
||||
osnr.lambda_c_nm = lambda_c;
|
||||
osnr.bw_signal_nm = options.bw_signal_nm;
|
||||
osnr.ref_bw_nm = options.ref_bw_nm;
|
||||
|
||||
if options.debugplots
|
||||
figure(100)
|
||||
xline(lambda_c,'LineWidth',2,'DisplayName','Max. OSA Value','Color',[0.8,0.8,0.8])
|
||||
obj.plot("DisplayName",'OSNR','FigureNumber',100);
|
||||
yline(osnr.p_ase_db,'LineWidth',2,'DisplayName','Signal Estimation');
|
||||
yline(osnr.p_sig_db,'LineWidth',2,'DisplayName','Noise Estimation');
|
||||
scatter(obj.lambda_nm(idx_noise),obj.psd_dBm(idx_noise),'Marker','.','LineWidth',4,'DisplayName','Noise Samples','MarkerEdgeColor','red');
|
||||
scatter(obj.lambda_nm(idx_signal),obj.psd_dBm(idx_signal),'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','green');
|
||||
scatter(obj.lambda_nm(idx_signal),ase_psd,'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','magenta');
|
||||
ylim([min(obj.psd_dBm), max([obj.psd_dBm,osnr.p_sig_db+5])]);
|
||||
|
||||
figure(101);hold on
|
||||
xline(lambda_c,'LineWidth',2,'DisplayName','Max. OSA Value','Color',[0.8,0.8,0.8])
|
||||
xline(lambda_c - options.bw_signal_nm/2,'LineWidth',2,'DisplayName','CW - 0.5 BW','Color',[0.6,0.6,0.8])
|
||||
xline(lambda_c + options.bw_signal_nm/2,'LineWidth',2,'DisplayName','CW + 0.5 BW','Color',[0.6,0.8,0.8])
|
||||
obj.plot("DisplayName",'OSNR','FigureNumber',101);
|
||||
yline(osnr.p_ase_db,'LineWidth',2,'DisplayName','Signal Estimation');
|
||||
yline(osnr.p_sig_db,'LineWidth',2,'DisplayName','Noise Estimation');
|
||||
scatter(obj.lambda_nm(idx_signal),obj.psd_dBm(idx_signal),'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','green');
|
||||
scatter(obj.lambda_nm(idx_signal),ase_psd,'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','magenta');
|
||||
xlim([lambda_c - options.bw_signal_nm, lambda_c + options.bw_signal_nm])
|
||||
ylim([min(obj.psd_dBm), max([obj.psd_dBm,osnr.p_sig_db+5])]);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
methods (Access = protected)
|
||||
function v = connect(obj)
|
||||
v = visadev(obj.visaAddress);
|
||||
end
|
||||
end
|
||||
end
|
||||
168
Classes/05_Lab/OptPowerMeter8153A.m
Normal file
168
Classes/05_Lab/OptPowerMeter8153A.m
Normal file
@@ -0,0 +1,168 @@
|
||||
classdef OptPowerMeter8153A < handle
|
||||
|
||||
% Code for HEWLETT-PACKARD 8153A Optical Power Meter (2-channel).
|
||||
% https://www.artisantg.com/info/Agilent_8153A_Manual.pdf?srsltid=AfmBOoqTe38sbGRNHUFXjw5uO6Br7I7aSfgAdRpx-hVvM4sTIhFYyD5x
|
||||
% PAGE 166++
|
||||
|
||||
% Typical usage:
|
||||
% pm = HPPowerMeter8153A( ...
|
||||
% 'active', [false true], ...
|
||||
% 'wavelength_nm', [1550 1310] );
|
||||
%
|
||||
% pm.setWavelength([1550 1310]); % only CH2 is modified
|
||||
% p = pm.readPower(); % reads only active channels
|
||||
|
||||
|
||||
properties (Access = public)
|
||||
active logical = [true true] % channel enable
|
||||
wavelength_nm double = [1550 1550] % per-channel wavelength [nm]
|
||||
power_dBm double = [NaN NaN] % last read power
|
||||
offset_dB double = [0 0] % per-channel power offset [dB]
|
||||
avgTime_s double = [0.2 0.2];
|
||||
end
|
||||
|
||||
properties (Constant)
|
||||
avgTime_min_s = 0.02 % 20 ms
|
||||
avgTime_max_s = 3600 % 1 hour
|
||||
end
|
||||
|
||||
properties (Access = protected)
|
||||
visaAddress string = "GPIB1::22::INSTR"
|
||||
numChannels double = 2
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = OptPowerMeter8153A(options)
|
||||
arguments
|
||||
options.visaAddress string = "GPIB1::22::INSTR"
|
||||
options.active logical = [true true]
|
||||
options.wavelength_nm double = [1550 1550]
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for k = 1:numel(fn)
|
||||
obj.(fn{k}) = options.(fn{k});
|
||||
end
|
||||
|
||||
assert(numel(obj.active)==obj.numChannels)
|
||||
assert(numel(obj.wavelength_nm)==obj.numChannels)
|
||||
|
||||
v = obj.connect();
|
||||
writeline(v,"*IDN?");
|
||||
disp(readline(v));
|
||||
pause(0.1);
|
||||
delete(v);
|
||||
pause(0.1);
|
||||
|
||||
obj.setWavelength(obj.wavelength_nm);
|
||||
end
|
||||
|
||||
function setWavelength(obj, lambda_nm)
|
||||
if nargin > 1
|
||||
obj.wavelength_nm = lambda_nm;
|
||||
end
|
||||
|
||||
v = obj.connect();
|
||||
for ch = 1:obj.numChannels
|
||||
if obj.active(ch)
|
||||
writeline(v, ...
|
||||
sprintf("SENS%d:POW:WAVE %gNM", ch, obj.wavelength_nm(ch)));
|
||||
end
|
||||
end
|
||||
pause(0.1);
|
||||
delete(v);
|
||||
pause(0.1);
|
||||
end
|
||||
|
||||
function p = readPower(obj)
|
||||
v = obj.connect();
|
||||
for ch = 1:obj.numChannels
|
||||
if obj.active(ch)
|
||||
writeline(v, sprintf("READ%d:POW?", ch));
|
||||
obj.power_dBm(ch) = str2double(readline(v));
|
||||
else
|
||||
obj.power_dBm(ch) = NaN;
|
||||
end
|
||||
end
|
||||
delete(v);
|
||||
p = obj.power_dBm;
|
||||
end
|
||||
|
||||
function setOffsetCorrection(obj, offset_dB)
|
||||
obj.offset_dB = offset_dB;
|
||||
|
||||
v = obj.connect();
|
||||
for ch = 1:obj.numChannels
|
||||
if obj.active(ch)
|
||||
writeline(v, ...
|
||||
sprintf("SENS%d:CORR:LOSS:INP:MAGN %gDB", ch, obj.offset_dB(ch)));
|
||||
end
|
||||
end
|
||||
delete(v);
|
||||
end
|
||||
|
||||
function offset_dB = getOffsetCorrection(obj)
|
||||
v = obj.connect();
|
||||
offset_dB = NaN(1,obj.numChannels);
|
||||
for ch = 1:obj.numChannels
|
||||
if obj.active(ch)
|
||||
writeline(v, sprintf("SENS%d:CORR:LOSS:INP:MAGN?", ch));
|
||||
offset_dB(ch) = str2double(readline(v));
|
||||
end
|
||||
end
|
||||
delete(v);
|
||||
obj.offset_dB = offset_dB;
|
||||
end
|
||||
|
||||
function setAvgTime(obj, avgTime_s)
|
||||
|
||||
if any(avgTime_s < obj.avgTime_min_s) || any(avgTime_s > obj.avgTime_max_s)
|
||||
|
||||
oldv = avgTime_s;
|
||||
|
||||
avgTime_s = max(avgTime_s, obj.avgTime_min_s);
|
||||
avgTime_s = min(avgTime_s, obj.avgTime_max_s);
|
||||
|
||||
fprintf('[HPPowerMeter8153A] Averaging time clipped:\n');
|
||||
for ch = 1:obj.numChannels
|
||||
if oldv(ch) ~= avgTime_s(ch)
|
||||
fprintf(' CH%d: %.3g s → %.3g s (allowed %.3g … %.3g s)\n', ...
|
||||
ch, oldv(ch), avgTime_s(ch), ...
|
||||
obj.avgTime_min_s, obj.avgTime_max_s);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
obj.avgTime_s = avgTime_s;
|
||||
|
||||
v = obj.connect();
|
||||
for ch = 1:obj.numChannels
|
||||
if obj.active(ch) && ~isnan(obj.avgTime_s(ch))
|
||||
writeline(v, ...
|
||||
sprintf("SENS%d:POW:ATIME %g", ch, obj.avgTime_s(ch)));
|
||||
end
|
||||
end
|
||||
delete(v);
|
||||
end
|
||||
|
||||
function avgTime_s = getAvgTime(obj)
|
||||
v = obj.connect();
|
||||
avgTime_s = NaN(1,obj.numChannels);
|
||||
for ch = 1:obj.numChannels
|
||||
if obj.active(ch)
|
||||
writeline(v, sprintf("SENS%d:POW:ATIME?", ch));
|
||||
avgTime_s(ch) = str2double(readline(v));
|
||||
end
|
||||
end
|
||||
delete(v);
|
||||
obj.avgTime_s = avgTime_s;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
methods (Access = protected)
|
||||
function v = connect(obj)
|
||||
v = visadev(obj.visaAddress);
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,7 +20,7 @@ classdef Thor_PDFA < handle
|
||||
function obj = Thor_PDFA(options)
|
||||
|
||||
arguments
|
||||
options.serialport_number = 'COM12'
|
||||
options.serialport_number = 'COM18'
|
||||
options.safety_mode = 1;
|
||||
end
|
||||
|
||||
@@ -37,7 +37,7 @@ classdef Thor_PDFA < handle
|
||||
end
|
||||
|
||||
function o = connectSerial(obj)
|
||||
|
||||
o = [];
|
||||
try
|
||||
% Connect to the PDFA
|
||||
o = serialport(obj.serialport_number, 9600);
|
||||
@@ -62,10 +62,14 @@ classdef Thor_PDFA < handle
|
||||
function getStatus(obj)
|
||||
|
||||
o = obj.connectSerial();
|
||||
|
||||
if ~isempty(o)
|
||||
obj.getStatus_(o);
|
||||
if obj.safety_mode
|
||||
obj
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1,20 +1,50 @@
|
||||
function waitUntilClick()
|
||||
% Create the UI figure
|
||||
fig = uifigure('Name', 'Pause Execution', 'Position', [100 100 300 150]);
|
||||
function waitUntilClick(options)
|
||||
|
||||
% Create a label to inform the user
|
||||
uilabel(fig, 'Position', [50 80 200 40], 'Text', 'Click "Continue" to proceed', ...
|
||||
'FontSize', 14, 'HorizontalAlignment', 'center');
|
||||
% use like this to stop any computation and wait for user to press ENTER or
|
||||
% Continue button. I used this in the lab to wait until smth settled or I
|
||||
% set a device to a certain operating point...
|
||||
|
||||
% USAGE:
|
||||
% waitUntilClick;
|
||||
|
||||
% OPTIONAL: provide input text that is prompted :-)
|
||||
% usrcmd = sprintf('Laser to %d dBm',p);
|
||||
% waitUntilClick('Text',usrcmd);
|
||||
|
||||
|
||||
arguments
|
||||
options.Text string = "Click ""Continue"" to proceed"
|
||||
end
|
||||
|
||||
% Create the UI figure
|
||||
fig = uifigure('Name', 'Pause Execution', ...
|
||||
'Position', [100 100 300 150], ...
|
||||
'KeyPressFcn', @(src,event) keyHandler(event));
|
||||
|
||||
% Create a label with custom text
|
||||
uilabel(fig, ...
|
||||
'Position', [30 70 240 60], ...
|
||||
'Text', options.Text, ...
|
||||
'FontSize', 14, ...
|
||||
'HorizontalAlignment', 'center', ...
|
||||
'WordWrap','on');
|
||||
|
||||
% Create the "Continue" button
|
||||
continueButton = uibutton(fig, 'push', 'Text', 'Continue', 'Position', [100 20 100 40], ...
|
||||
uibutton(fig, 'push', ...
|
||||
'Text', 'Continue', ...
|
||||
'Position', [100 20 100 40], ...
|
||||
'ButtonPushedFcn', @(src, event) closeWindow());
|
||||
|
||||
% Function to close the window when "Continue" is clicked
|
||||
function closeWindow()
|
||||
delete(fig); % Close the figure window
|
||||
delete(fig);
|
||||
end
|
||||
|
||||
% Wait until the figure is closed to resume execution
|
||||
function keyHandler(event)
|
||||
if strcmp(event.Key,'return') || strcmp(event.Key,'enter')
|
||||
closeWindow();
|
||||
end
|
||||
end
|
||||
|
||||
% Wait until the figure is closed
|
||||
waitfor(fig);
|
||||
end
|
||||
20
Functions/Metrics/noiseFigureFromOSNR.m
Normal file
20
Functions/Metrics/noiseFigureFromOSNR.m
Normal file
@@ -0,0 +1,20 @@
|
||||
function NF_dB = noiseFigureFromOSNR(Pin_dBm, OSNR_dB, lambda_nm, Bref_nm)
|
||||
|
||||
if nargin < 4
|
||||
Bref_nm = 0.1; % OSNR reference bandwidth
|
||||
end
|
||||
|
||||
h = 6.62607015e-34; % Planck [J*s]
|
||||
c = 299792458; % speed of light [m/s]
|
||||
|
||||
lambda = lambda_nm * 1e-9;
|
||||
nu = c / lambda;
|
||||
|
||||
% convert reference bandwidth from nm to Hz
|
||||
Bref_Hz = Bref_nm*1e-9 * c / lambda^2;
|
||||
|
||||
% ASE noise power density in dBm
|
||||
Pn_dBm = 10*log10(h*nu*Bref_Hz) + 30;
|
||||
|
||||
NF_dB = Pin_dBm - OSNR_dB - Pn_dBm;
|
||||
end
|
||||
BIN
projects.mat
Normal file
BIN
projects.mat
Normal file
Binary file not shown.
Binary file not shown.
BIN
projects/Lab_analysis/aeon_soa_measurement_pump_level_sweep.mat
Normal file
BIN
projects/Lab_analysis/aeon_soa_measurement_pump_level_sweep.mat
Normal file
Binary file not shown.
127
projects/Lab_analysis/amplifier_characterization.m
Normal file
127
projects/Lab_analysis/amplifier_characterization.m
Normal file
@@ -0,0 +1,127 @@
|
||||
% Silas Amplifier characterization
|
||||
|
||||
laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib');
|
||||
laser.getLaserInfo();
|
||||
laser.disableLaser;
|
||||
laser.setWavelength(1310);
|
||||
laser.enableLaser;
|
||||
|
||||
% pm = OptPowerMeter8153A("active",[0,1],"wavelength_nm",[1550,1310]);
|
||||
% pm.readPower
|
||||
|
||||
% pdfa = Thor_PDFA();
|
||||
%%
|
||||
params.laserpower = [-30,-20,-10,0];
|
||||
params.lambda = [1260:5:1360]; %calcWavelengthPlan(16, 400e9 , 1310);
|
||||
params.pump = [50,75,100];
|
||||
|
||||
wh = DataStorage(params);
|
||||
wh.addStorage("spectrum_osa");
|
||||
wh.addStorage("wavelength_osa");
|
||||
wh.addStorage("psig_osa");
|
||||
wh.addStorage("pase_osa");
|
||||
wh.addStorage("osnr_osa");
|
||||
wh.addStorage("amp_gain");
|
||||
wh.addStorage("psig_total");
|
||||
|
||||
cols = linspecer(numel(params.laserpower));
|
||||
ccnt = 0;
|
||||
for p = params.laserpower
|
||||
ccnt=ccnt+1;
|
||||
laser.setWavelength(params.lambda(1));
|
||||
usrcmd = sprintf('Laser to %d dBm',p);
|
||||
waitUntilClick('Text',usrcmd);
|
||||
|
||||
for pmp = params.pump
|
||||
% pdfa.setPumpLevel(pmp);
|
||||
usrcmd = sprintf('Amp to %d mA',2*pmp);
|
||||
waitUntilClick('Text',usrcmd);
|
||||
|
||||
for w = params.lambda
|
||||
laser.setWavelength(w)
|
||||
% pm.setWavelength([1550, w]);
|
||||
span_nm = 5;
|
||||
lambda_c_nm = w;
|
||||
osa = OSA_Advantest( ...
|
||||
'span_nm', 40, ...
|
||||
'lambda_c_nm', w, ...
|
||||
'resolution_nm', 0.1, ...
|
||||
'sampling_points', 1001 );
|
||||
osa.configure();
|
||||
|
||||
|
||||
|
||||
% OSA
|
||||
[lambda_nm, psd_dBm] = osa.measure();
|
||||
osnr = osa.computeOSNR('bw_signal_nm',0.01,'ref_bw_nm',0.1,'debugplots',0,...
|
||||
'carrier_frequency',w,'noise_guard_nm',1,'noise_window_nm',1);
|
||||
|
||||
osa.plot("FigureNumber",pmp+1,"Color",cols(ccnt,:));
|
||||
xlabel('Wavelength [nm]');
|
||||
ylabel('Opt. Spectrum [dBm]');
|
||||
xlim([params.lambda(1)-5 params.lambda(end)+5])
|
||||
grid minor
|
||||
|
||||
% Powermeter
|
||||
totalpower=10*log10(sum(10.^(psd_dBm./10)));
|
||||
% [totalpower]=pm.readPower;
|
||||
% totalpower=totalpower(2) + 20; %20dB attenuation with eigenlight to satisfy 3dB max of PMeter
|
||||
|
||||
wh.addValueToStorage(lambda_nm,'wavelength_osa',p,w,pmp);
|
||||
wh.addValueToStorage(psd_dBm,'spectrum_osa',p,w,pmp);
|
||||
wh.addValueToStorage(osnr.p_ase_db,'pase_osa',p,w,pmp);
|
||||
wh.addValueToStorage(osnr.p_sig_db,'psig_osa',p,w,pmp);
|
||||
wh.addValueToStorage(osnr,'osnr_osa',p,w,pmp);
|
||||
wh.addValueToStorage(totalpower,'psig_total',p,w,pmp);
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
% before tuning all the way back to 1270nm set PDFA off
|
||||
% pdfa.setPumpLevel(0);
|
||||
% pdfa.disablePDFA;
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
%% OSNR
|
||||
|
||||
figure(2);hold on
|
||||
for p = [-30:10:0]
|
||||
pmp = 100;
|
||||
osnr = wh.getStoValue('osnr_osa',p,params.lambda,pmp);
|
||||
osnr_measured_dB = cellfun(@(x) x.measured_dB, osnr);
|
||||
osnr_direct_dB = cellfun(@(x) x.direct_dB, osnr);
|
||||
plot(params.lambda,osnr_direct_dB,'DisplayName',sprintf('P_{in}: %d dB ',p));
|
||||
end
|
||||
legend
|
||||
|
||||
%% P out
|
||||
|
||||
figure(3);hold on
|
||||
for p = [-30:10:0]
|
||||
pmp = 100;
|
||||
psig_osa = wh.getStoValue('psig_osa',p,params.lambda,pmp)+3;
|
||||
pl=plot(params.lambda,psig_osa,'DisplayName',sprintf('P_{signal}: %d dB ',p));
|
||||
|
||||
psig_powermeter = wh.getStoValue('psig_total',p,params.lambda,pmp)+3;
|
||||
plot(params.lambda,psig_powermeter,'DisplayName',sprintf('P_{total}: %d dB ',p),'LineStyle','--','Color',pl.Color);
|
||||
|
||||
yline(p,'Color',pl.Color,'DisplayName',sprintf('P_{total}: %d dB ',p));
|
||||
end
|
||||
legend
|
||||
|
||||
%% GAIN
|
||||
|
||||
figure(3);hold on
|
||||
for p = [-30:10:0]
|
||||
|
||||
pmp = 50;
|
||||
psig_osa = wh.getStoValue('psig_osa',p,params.lambda,pmp);
|
||||
% psig_powermeter = wh.getStoValue('psig_powermeter',p,params.lambda,pmp);
|
||||
|
||||
g = psig_osa-p;
|
||||
pl=plot(params.lambda,g,'DisplayName',sprintf('P_{in}: %d dB ',p));
|
||||
end
|
||||
legend
|
||||
111
projects/Lab_analysis/amplifier_input_output_curve.m
Normal file
111
projects/Lab_analysis/amplifier_input_output_curve.m
Normal file
@@ -0,0 +1,111 @@
|
||||
% Silas Amplifier characterization
|
||||
|
||||
laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib');
|
||||
laser.getLaserInfo();
|
||||
laser.disableLaser;
|
||||
laser.setWavelength(1310);
|
||||
laser.enableLaser;
|
||||
|
||||
pdfa = Thor_PDFA();
|
||||
%%
|
||||
params.laserpower = [-30,-20,-10,0];
|
||||
params.lambda = [1310]; %calcWavelengthPlan(16, 400e9 , 1310);
|
||||
params.pump = [0:5:100];
|
||||
|
||||
wh = DataStorage(params);
|
||||
wh.addStorage("spectrum_osa");
|
||||
wh.addStorage("wavelength_osa");
|
||||
wh.addStorage("psig_osa");
|
||||
wh.addStorage("pase_osa");
|
||||
wh.addStorage("osnr_osa");
|
||||
wh.addStorage("amp_gain");
|
||||
wh.addStorage("psig_total");
|
||||
|
||||
cols = linspecer(numel(params.laserpower));
|
||||
ccnt = 0;
|
||||
for p = params.laserpower
|
||||
ccnt=ccnt+1;
|
||||
laser.setWavelength(params.lambda(1));
|
||||
usrcmd = sprintf('Laser to %d dBm',p);
|
||||
waitUntilClick('Text',usrcmd);
|
||||
|
||||
|
||||
for w = params.lambda
|
||||
laser.setWavelength(w)
|
||||
% pm.setWavelength([1550, w]);
|
||||
span_nm = 5;
|
||||
lambda_c_nm = w;
|
||||
osa = OSA_Advantest( ...
|
||||
'span_nm', 40, ...
|
||||
'lambda_c_nm', w, ...
|
||||
'resolution_nm', 0.1, ...
|
||||
'sampling_points', 1001 );
|
||||
osa.configure();
|
||||
|
||||
|
||||
for pmp = params.pump
|
||||
% pdfa.setPumpLevel(pmp);
|
||||
usrcmd = sprintf('Amp to %d %',pmp);
|
||||
waitUntilClick('Text',usrcmd);
|
||||
|
||||
% OSA
|
||||
[lambda_nm, psd_dBm] = osa.measure();
|
||||
osnr = osa.computeOSNR('bw_signal_nm',0.01,'ref_bw_nm',0.1,'debugplots',0,...
|
||||
'carrier_frequency',w,'noise_guard_nm',1,'noise_window_nm',1);
|
||||
|
||||
osa.plot("FigureNumber",abs(p)+10,"Color",cols(ccnt,:));
|
||||
xlabel('Wavelength [nm]');
|
||||
ylabel('Opt. Spectrum [dBm]');
|
||||
grid minor
|
||||
|
||||
% Powermeter
|
||||
totalpower=10*log10(sum(10.^(psd_dBm./10)));
|
||||
% [totalpower]=pm.readPower;
|
||||
% totalpower=totalpower(2) + 20; %20dB attenuation with eigenlight to satisfy 3dB max of PMeter
|
||||
|
||||
wh.addValueToStorage(lambda_nm,'wavelength_osa',p,w,pmp);
|
||||
wh.addValueToStorage(psd_dBm,'spectrum_osa',p,w,pmp);
|
||||
wh.addValueToStorage(osnr.p_ase_db,'pase_osa',p,w,pmp);
|
||||
wh.addValueToStorage(osnr.p_sig_db,'psig_osa',p,w,pmp);
|
||||
wh.addValueToStorage(osnr,'osnr_osa',p,w,pmp);
|
||||
wh.addValueToStorage(totalpower,'psig_total',p,w,pmp);
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
%% P out
|
||||
|
||||
figure(3);hold on
|
||||
for p = [-30:10:0]
|
||||
|
||||
psig_osa = wh.getStoValue('psig_osa',p,params.lambda,params.pump);
|
||||
pl=plot(params.pump,psig_osa,'DisplayName',sprintf('P_{signal}: %d dB ',p));
|
||||
|
||||
psig_total = wh.getStoValue('psig_total',p,params.lambda,params.pump);
|
||||
plot(params.pump,psig_total,'DisplayName',sprintf('P_{total}: %d dB ',p),'LineStyle','--','Color',pl.Color);
|
||||
|
||||
pase = wh.getStoValue('pase_osa',p,params.lambda,params.pump);
|
||||
plot(params.pump,pase,'DisplayName',sprintf('P_{ASE}: %d dB ',p),'LineStyle',':','Color',pl.Color);
|
||||
|
||||
|
||||
end
|
||||
legend
|
||||
|
||||
|
||||
|
||||
%% Gain
|
||||
|
||||
figure(4);hold on
|
||||
for p = [-30:10:0]
|
||||
|
||||
% Die -2dB sind Korrekturfaktor aus dem Setup! Gilt für PDFA und SOA
|
||||
psig_osa = wh.getStoValue('psig_osa',p,params.lambda,params.pump);
|
||||
pl=plot(params.pump,psig_osa-(p-2),'DisplayName',sprintf('Gain: %d dB ',p));
|
||||
|
||||
psig_total = wh.getStoValue('psig_total',p,params.lambda,params.pump);
|
||||
pl=plot(params.pump,psig_total-(p-2),'DisplayName',sprintf('Gain: %d dB ',p),'Color',pl.Color,'LineStyle','--');
|
||||
|
||||
end
|
||||
legend
|
||||
BIN
projects/Lab_analysis/available_output_power_exfo_t100_oband.fig
Normal file
BIN
projects/Lab_analysis/available_output_power_exfo_t100_oband.fig
Normal file
Binary file not shown.
Binary file not shown.
54
projects/Lab_analysis/laser_characterization.m
Normal file
54
projects/Lab_analysis/laser_characterization.m
Normal file
@@ -0,0 +1,54 @@
|
||||
laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib');
|
||||
laser.getLaserInfo();
|
||||
|
||||
pm = OptPowerMeter8153A("active",[0,1],"wavelength_nm",[1550,start_wavelength]);
|
||||
pm.readPower
|
||||
|
||||
laserparams.lambda = [1260:2:1360];
|
||||
laserparams.laserpower = [0,3,6,8,10];
|
||||
laser_output_wh = DataStorage(laserparams);
|
||||
laser_output_wh.addStorage("plaser");
|
||||
|
||||
pm = OptPowerMeter8153A("active",[0,1],"wavelength_nm",[1550,1310]);
|
||||
laser.setWavelength(1310);
|
||||
|
||||
cw_pwr = NaN(numel(laserparams.laserpower),numel(laserparams.lambda));
|
||||
row = 1;
|
||||
for p = laserparams.laserpower
|
||||
laser.setPower(p);
|
||||
cnt = 1;
|
||||
for w = laserparams.lambda
|
||||
laser.setWavelength(w);
|
||||
pm.setWavelength([1550,w]);
|
||||
|
||||
for i = 1:20
|
||||
[totalpower]=pm.readPower;
|
||||
cw_measurement(i)=totalpower(2)+10;
|
||||
cw_pwr(row,cnt) = mean(cw_measurement);
|
||||
pause(3);
|
||||
end
|
||||
|
||||
laser_output_wh.addValueToStorage(cw_measurement,'plaser',w,p);
|
||||
|
||||
figure(2027);clf
|
||||
plot(laserparams.lambda,cw_pwr(1:row,:),'Marker','*');
|
||||
xlabel('Wavelength [nm]');
|
||||
ylabel('Laser Output Power [dBm]');
|
||||
ylim([0, 11]);
|
||||
xlim([laserparams.lambda(1) laserparams.lambda(end)])
|
||||
grid minor
|
||||
|
||||
cnt = cnt+1;
|
||||
end
|
||||
row = row+1;
|
||||
end
|
||||
|
||||
figure();hold on
|
||||
for p = laserparams.laserpower
|
||||
plot(laserparams.lambda,laser_output_wh.getStoValue('plaser',laserparams.lambda,p))
|
||||
end
|
||||
xlabel('Wavelength [nm]');
|
||||
ylabel('Laser Output Power [dBm]');
|
||||
ylim([0, 11]);
|
||||
xlim([laserparams.lambda(1) laserparams.lambda(end)])
|
||||
grid minor
|
||||
BIN
projects/Lab_analysis/laser_outputpower_sweep_1.mat
Normal file
BIN
projects/Lab_analysis/laser_outputpower_sweep_1.mat
Normal file
Binary file not shown.
Binary file not shown.
BIN
projects/Lab_analysis/pdfa_thorlabs_pin_vs_pout.fig
Normal file
BIN
projects/Lab_analysis/pdfa_thorlabs_pin_vs_pout.fig
Normal file
Binary file not shown.
BIN
projects/Lab_analysis/pdfa_thorlabs_pout.fig
Normal file
BIN
projects/Lab_analysis/pdfa_thorlabs_pout.fig
Normal file
Binary file not shown.
BIN
projects/Lab_analysis/thorlabs_pdfa_100prozent_sweep.mat
Normal file
BIN
projects/Lab_analysis/thorlabs_pdfa_100prozent_sweep.mat
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,15 +1,15 @@
|
||||
%% Laser
|
||||
start_wavelength = 1550;
|
||||
laser = Exfo_laser("mainframe_channel",3,"safety_mode",0,"connection_id",'10','lab_interface','gpib');
|
||||
start_wavelength = 1310;
|
||||
laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib');
|
||||
laser.getLaserInfo();
|
||||
|
||||
laser.setWavelength(start_wavelength)
|
||||
laser.setPower(0);
|
||||
|
||||
laser.enableLaser();
|
||||
for l = 1550:1560
|
||||
laser.setWavelength(l);
|
||||
end
|
||||
% for l = 1550:1560
|
||||
% laser.setWavelength(l);
|
||||
% end
|
||||
|
||||
laser.disableLaser();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user