368 KiB
368 KiB
diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m
index e06c41f..f09a03e 100644
--- a/Classes/00_signals/Signal.m
+++ b/Classes/00_signals/Signal.m
@@ -172,11 +172,9 @@ classdef Signal
hold on;
if isempty(options.color)
- % plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Marker', '.', 'LineStyle','none', 'MarkerSize', 0.1);
- plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1);
+ plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Marker', '.', 'LineStyle','none', 'MarkerSize', 0.1);
else
- % plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Marker', '.', 'LineStyle','none', 'MarkerSize', 0.1,'Color',options.color);
- plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Color',options.color);
+ plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Marker', '.', 'LineStyle','none', 'MarkerSize', 0.1,'Color',options.color);
end
% 2 c)
% - xlabel if not already here: time in readable format (1 ms and not 1e-3 s)
diff --git a/Classes/02_optical/DP_Fiber.m b/Classes/02_optical/DP_Fiber.m
index a5f3dce..c1f08ea 100644
--- a/Classes/02_optical/DP_Fiber.m
+++ b/Classes/02_optical/DP_Fiber.m
@@ -24,6 +24,8 @@ classdef DP_Fiber
SS_dzmax % [m] max dz (adaptive SSFM)
SS_dzmin % [m] min dz (adaptive SSFM)
n_waveplates % number of PMD waveplates
+ useGPU % GPU acceleration: true, false, or 'auto' (default)
+ useSingle % Use single precision on GPU (default: false)
% ---- Internal state (persistent between calls) ----
state % struct mirroring legacy 'state'
@@ -56,6 +58,8 @@ classdef DP_Fiber
options.SS_dzmax = 2e4 % m
options.SS_dzmin = 100 % m
options.n_waveplates = 100
+ options.useGPU = 'auto' % 'auto', true, or false
+ options.useSingle = false % single precision GPU
end
% Copy provided options into properties
@@ -208,7 +212,7 @@ classdef DP_Fiber
% Frequency-dependent PMD phase term (legacy form)
st.brf.db0 = (R.rand(st.wave_plates,1)*2*pi - pi) * brf_multiplier;
st.brf.db1 = sqrt(3*pi/8)*(st.dgd/obj.fa)/st.wave_plates .* st.omega;
- st.brf.simdgd = 0;
+ st.brf.simdgd = 0;
% cumsum used in legacy only for debug; keep compatibility variable:
~cumsum(st.brf.db0); % no-op to mirror legacy path
@@ -228,7 +232,18 @@ classdef DP_Fiber
x_in = signal_in(:,1).';
y_in = signal_in(:,2).';
- [x_out, y_out, obj.state] = CNLSE_plain(x_in, y_in, obj.state);
+ % Determine GPU usage
+ if ischar(obj.useGPU) || isstring(obj.useGPU)
+ if strcmpi(obj.useGPU, 'auto')
+ gpuFlag = []; % Let CNLSE_plain auto-detect
+ else
+ error('DP_Fiber:InvalidGPU', 'useGPU must be true, false, or ''auto''');
+ end
+ else
+ gpuFlag = logical(obj.useGPU);
+ end
+
+ [x_out, y_out, obj.state] = CNLSE_plain(x_in, y_in, obj.state, gpuFlag, obj.useSingle);
obj.state.propagated_length = obj.state.propagated_length + obj.state.L;
diff --git a/Classes/02_optical/Optical_Demultiplex.m b/Classes/02_optical/Optical_Demultiplex.m
index 7d7c39e..8e272d2 100644
--- a/Classes/02_optical/Optical_Demultiplex.m
+++ b/Classes/02_optical/Optical_Demultiplex.m
@@ -42,7 +42,7 @@ classdef Optical_Demultiplex < handle
function signalclasses_out = process(obj, signalclass_in)
- % ---- Infer wavelength: either given or from input total signal
+ % ---- Infer wavelength: either given or from input total signal
if isempty(obj.wavelengthplan)
obj.wavelengthplan = signalclass_in.lambda; %meter
else
@@ -81,7 +81,7 @@ classdef Optical_Demultiplex < handle
obj
signal_in
end
-
+
w = obj.fs_out ./ obj.fs_in ;
blocklen_in = length(signal_in);
blocklen_out = w*blocklen_in;
@@ -119,30 +119,31 @@ classdef Optical_Demultiplex < handle
N = size(lo,1);
C = size(lo,2);
- x_envelopes = zeros(N, C, 'like', signal_in);
- y_envelopes = zeros(N, C, 'like', signal_in);
+ % ---- VECTORIZED: Process all channels in parallel ----
+ % Batched FFT operates on each column simultaneously on GPU
- s1 = signal_in(:,1);
- s2 = signal_in(:,2);
+ % Extract polarization signals
+ s1 = signal_in(:,1); % X polarization [N×1]
+ s2 = signal_in(:,2); % Y polarization [N×1]
- % Reusable work buffers (avoid reallocations)
- wrk_time = zeros(N,1, 'like', signal_in);
- wrk_freq = zeros(N,1, 'like', signal_in);
+ % Broadcast signal to all channels and multiply with LO
+ % s1, s2 are [N×1], lo is [N×C] → result is [N×C]
+ x_mixed = att .* s1 .* lo; % [N×C]
+ y_mixed = att .* s2 .* lo; % [N×C]
+
+ % Batched FFT: each column computed in parallel
+ x_freq = fft(x_mixed); % [N×C]
+ y_freq = fft(y_mixed); % [N×C]
+
+ % Apply filter (H is [N×1], broadcasts across columns)
+ x_filtered = x_freq .* H; % [N×C]
+ y_filtered = y_freq .* H; % [N×C]
+
+ % Batched IFFT
+ x_envelopes = ifft(x_filtered); % [N×C]
+ y_envelopes = ifft(y_filtered); % [N×C]
- for c = 1:C
- % ---- X branch ----
- wrk_time(:) = att .* s1 .* lo(:,c); % N×1
- wrk_freq(:) = fft(wrk_time); % N×1
- wrk_freq(:) = wrk_freq .* H; % N×1
- x_envelopes(:,c) = ifft(wrk_freq); % N×1
- % ---- Y branch ----
- wrk_time(:) = att .* s2 .* lo(:,c);
- wrk_freq(:) = fft(wrk_time);
- wrk_freq(:) = wrk_freq .* H;
- y_envelopes(:,c) = ifft(wrk_freq);
- end
-
end
end
end
diff --git a/Classes/02_optical/Optical_Multiplex.m b/Classes/02_optical/Optical_Multiplex.m
index 1d722b0..7fc8a33 100644
--- a/Classes/02_optical/Optical_Multiplex.m
+++ b/Classes/02_optical/Optical_Multiplex.m
@@ -1,10 +1,10 @@
classdef Optical_Multiplex < handle
% Takes a cell array of signals
- % returns a total field signal
- % WDM spacing is given in wavelength plan OR via delta_F
-
+ % returns a total field signal
+ % WDM spacing is given in wavelength plan OR via delta_F
+
% The grid is stored in the output signal -> the demux will ideally
- % look this up and use this as the demux frequencies...
+ % look this up and use this as the demux frequencies...
% signal_cell = {Opt_sig_1, Opt_sig_2};
% Opt_sig_wdm = Optical_Multiplex("fs_in",Opt_sig.fs,"fs_out",4*Opt_sig.fs,...
@@ -117,7 +117,7 @@ classdef Optical_Multiplex < handle
% adapt frequency shifts to match the FFT grid! Find nearest grid point
[glitch(o),pos] = min(abs( freqaxis-obj.df_T(o) ));
obj.df_T(o) = freqaxis(pos);
-
+
polrots = [polrots, data_in{o}.polrot];
end
@@ -139,25 +139,36 @@ classdef Optical_Multiplex < handle
x_envelopes = NaN([blocklen_out N]);
y_envelopes = x_envelopes;
-
- for o = 1:N
-
- pha = mod(2*pi*(0:blocklen_out-1)*obj.df_T(o)/obj.fs_out,2*pi).';
- lo = cos(pha)+1i*sin(pha);
- data_in_resampled = data_in{o}.resample("fs_out",obj.fs_out);
- res_env = ifft(fft(data_in_resampled.signal(:,1)).*H);
- x_envelopes(:,o) = att.*res_env.*lo;
+ % ---- OPTIMIZED: Pre-compute all LO phases as [blocklen_out × N] matrix ----
+ time_idx = (0:blocklen_out-1).'; % [blocklen_out × 1]
+ lo_phases = mod(2*pi * time_idx * obj.df_T / obj.fs_out, 2*pi); % [blocklen_out × N]
+ lo_all = cos(lo_phases) + 1i*sin(lo_phases); % [blocklen_out × N]
- res_env = ifft(fft(data_in_resampled.signal(:,2)).*H);
- y_envelopes(:,o) = att.*res_env.*lo;
+ % Collect all resampled signals first (still requires loop due to cell array)
+ x_signals = zeros(blocklen_out, N);
+ y_signals = zeros(blocklen_out, N);
+ for o = 1:N
+ data_in_resampled = data_in{o}.resample("fs_out", obj.fs_out);
+ x_signals(:, o) = data_in_resampled.signal(:, 1);
+ y_signals(:, o) = data_in_resampled.signal(:, 2);
end
+ % ---- VECTORIZED: Batched FFT/IFFT for all channels ----
+ % Apply filter to all channels at once
+ x_filtered = ifft(fft(x_signals) .* H); % [blocklen_out × N]
+ y_filtered = ifft(fft(y_signals) .* H); % [blocklen_out × N]
+
+ % Apply attenuation and LO shift to all channels
+ x_envelopes = att .* x_filtered .* lo_all; % [blocklen_out × N]
+ y_envelopes = att .* y_filtered .* lo_all; % [blocklen_out × N]
+
data_out = data_in_resampled;
data_out.signal = [sum(x_envelopes,2), sum(y_envelopes,2)];
data_out.lambda = obj.lambda_T;
data_out.polrot = polrots;
+
end
end
diff --git a/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m b/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m
index cfa7294..463adca 100644
--- a/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m
+++ b/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m
@@ -1,41 +1,72 @@
-function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
-
-
-
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- % pre calculations
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
- state.common_beta=struct('X',0,'Y',0);
-
- for n=1:2
- % get current polarization name and contrary one
- curPol = state.polNames{n};
+function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state,useGPU,useSingle)
+
+% GPU auto-detection if not specified
+if nargin < 4 || isempty(useGPU)
+ useGPU = canUseGPU();
+end
+if nargin < 5 || isempty(useSingle)
+ useSingle = false;
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% pre calculations
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+state.common_beta=struct('X',0,'Y',0);
+
+for n=1:2
+ % get current polarization name and contrary one
+ curPol = state.polNames{n};
+
+ % extend linear transfer function depending on beta values for the current polarization
+ % Was ist der Sinn dieser komischen beta notation? zB. state.beta.X = [0.3142 0 -9.1105e-28 5.1068e-41]
+ for n_beta = 1:length(state.beta.(curPol))
+ state.common_beta.(curPol) = state.common_beta.(curPol) + state.beta.(curPol)(n_beta) * (state.omega).^(n_beta-1) / factorial(n_beta-1);
+ end
- % extend linear transfer function depending on beta values for the current polarization
- % Was ist der Sinn dieser komischen beta notation? zB. state.beta.X = [0.3142 0 -9.1105e-28 5.1068e-41]
- for n_beta = 1:length(state.beta.(curPol))
- state.common_beta.(curPol) = state.common_beta.(curPol) + state.beta.(curPol)(n_beta) * (state.omega).^(n_beta-1) / factorial(n_beta-1);
+ %opt_out_struct.(curPol)=opt_in_struct.(curPol).envelope;
+end
+
+beta_const = state.beta.('X')(1);
+beta_1 = state.beta.('X')(2);
+beta_2 = state.beta.('X')(3);
+beta_3 = state.beta.('X')(4);
+deltaomega = state.omega;
+beta_x = beta_const + beta_1 * deltaomega + 1/2 * beta_2 * deltaomega.^2 + 1/6 *beta_3 * deltaomega.^3;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% GPU Transfer (if enabled)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+if useGPU
+ % Convert to single precision if requested (faster on most GPUs)
+ if useSingle
+ opt_in_x = gpuArray(single(opt_in_x));
+ opt_in_y = gpuArray(single(opt_in_y));
+ state.common_beta.X = gpuArray(single(state.common_beta.X));
+ state.common_beta.Y = gpuArray(single(state.common_beta.Y));
+ state.brf.db1 = gpuArray(single(state.brf.db1));
+ state.brf.db0 = gpuArray(single(state.brf.db0));
+ for k = 1:numel(state.brf.matR)
+ state.brf.matR{k} = gpuArray(single(state.brf.matR{k}));
+ end
+ else
+ opt_in_x = gpuArray(opt_in_x);
+ opt_in_y = gpuArray(opt_in_y);
+ state.common_beta.X = gpuArray(state.common_beta.X);
+ state.common_beta.Y = gpuArray(state.common_beta.Y);
+ state.brf.db1 = gpuArray(state.brf.db1);
+ state.brf.db0 = gpuArray(state.brf.db0);
+ for k = 1:numel(state.brf.matR)
+ state.brf.matR{k} = gpuArray(state.brf.matR{k});
end
-
- %opt_out_struct.(curPol)=opt_in_struct.(curPol).envelope;
end
+end
+
- beta_const = state.beta.('X')(1);
- beta_1 = state.beta.('X')(2);
- beta_2 = state.beta.('X')(3);
- beta_3 = state.beta.('X')(4);
- deltaomega = state.omega;
- beta_x = beta_const + beta_1 * deltaomega + 1/2 * beta_2 * deltaomega.^2 + 1/6 *beta_3 * deltaomega.^3;
-
-% opt_in_x = gpuArray(opt_in_x);
-% opt_in_y = gpuArray(opt_in_y);
-
-
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- % Split Step Method
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Split Step Method
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% [opt_out_x,opt_out_y] = split_step_loop(state.L,opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin,...
% state.lin_z_test,state.corr_length,state.n_plates_done,state.missing_dz,state.brf,state.common_beta,....
@@ -44,35 +75,33 @@ function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
% [opt_out_x,opt_out_y] = split_step_loop_mex(state.L,opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin,...
% state.lin_z_test,state.corr_length,state.n_plates_done,state.missing_dz,state.brf,state.common_beta,....
% state.chi,state.manakov,state.beat_len);
-
- % get nonlinear step size
- [state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
- %[state.dz] = getNLstepsize_original(state,opt_out_struct);
-
- state.n_step = 0;
- state.z_prop = 0;
- state.test_dz = [];
- state.powers = [];
- tic
+% get nonlinear step size
+[state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
+%[state.dz] = getNLstepsize_original(state,opt_out_struct);
- while state.z_prop < state.L
+state.n_step = 0;
+state.z_prop = 0;
+state.test_dz = [];
+state.powers = [];
- % reduce step length (dz) if we are to overshoot the fiber length
- % (L) in the next step
- if state.z_prop + state.dz > state.L
- state.dz = state.L - state.z_prop;
- end
-
- % update step number (n)
- state.n_step=state.n_step+1;
+while state.z_prop < state.L
- % append current step length to logbook (dzs)
- state.dzs(state.n_step)=state.dz;
+ % reduce step length (dz) if we are to overshoot the fiber length
+ % (L) in the next step
+ if state.z_prop + state.dz > state.L
+ state.dz = state.L - state.z_prop;
+ end
+
+ % update step number (n)
+ state.n_step=state.n_step+1;
+
+ % append current step length to logbook (dzs)
+ state.dzs(state.n_step)=state.dz;
-
- % half linear step
- [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
+
+ % half linear step
+ [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,...
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X]...
@@ -82,12 +111,12 @@ function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X);
- % complete nonlinear step
-
- [opt_in_x,opt_in_y] = nl_step(opt_in_x,opt_in_y, state.dz, state.gamma, state.chi, state.manakov, state.beat_len ,state.alpha_lin.X, state.alpha_lin.Y);
-
- % half linear step
- [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
+ % complete nonlinear step
+
+ [opt_in_x,opt_in_y] = nl_step(opt_in_x,opt_in_y, state.dz, state.gamma, state.chi, state.manakov, state.beat_len ,state.alpha_lin.X, state.alpha_lin.Y);
+
+ % half linear step
+ [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,...
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X]...
@@ -97,24 +126,34 @@ function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X);
- % get nonlinear step size
- [state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
- %[state.dz] = getNLstepsize_original(state,opt_out_struct);
+ % get nonlinear step size
+ [state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
+ %[state.dz] = getNLstepsize_original(state,opt_out_struct);
-
- end
-
- toc
-
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% GPU Gather (if enabled)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+if useGPU
+ opt_out_x = gather(opt_in_x);
+ opt_out_y = gather(opt_in_y);
- opt_out_x = (opt_in_x);
- opt_out_y = (opt_in_y);
+ % Gather state arrays back to CPU
+ state.common_beta.X = gather(state.common_beta.X);
+ state.common_beta.Y = gather(state.common_beta.Y);
+ state.brf.db1 = gather(state.brf.db1);
+ state.brf.db0 = gather(state.brf.db0);
+ for k = 1:numel(state.brf.matR)
+ state.brf.matR{k} = gather(state.brf.matR{k});
+ end
+else
+ opt_out_x = opt_in_x;
+ opt_out_y = opt_in_y;
+end
-% opt_out_x = gather(opt_in_x);
-% opt_out_y = gather(opt_in_y);
-
end
\ No newline at end of file
diff --git a/Classes/02_optical/dp_fiber_lib/canUseGPU.m b/Classes/02_optical/dp_fiber_lib/canUseGPU.m
new file mode 100644
index 0000000..84666bb
--- /dev/null
+++ b/Classes/02_optical/dp_fiber_lib/canUseGPU.m
@@ -0,0 +1,20 @@
+function canUse = canUseGPU()
+%CANUSEGPU Check if a compatible GPU is available for parallel computing
+% Returns true if MATLAB Parallel Computing Toolbox is available and
+% a CUDA-capable GPU is detected.
+
+canUse = false;
+
+% Check if Parallel Computing Toolbox is installed
+if ~license('test', 'Distrib_Computing_Toolbox')
+ return;
+end
+
+% Check for GPU device
+try
+ gpu = gpuDevice();
+ canUse = gpu.DeviceSupported;
+catch
+ canUse = false;
+end
+end
diff --git a/Classes/02_optical/dp_fiber_lib/getNLstepsize.m b/Classes/02_optical/dp_fiber_lib/getNLstepsize.m
index a70edb9..fd42eba 100644
--- a/Classes/02_optical/dp_fiber_lib/getNLstepsize.m
+++ b/Classes/02_optical/dp_fiber_lib/getNLstepsize.m
@@ -1,25 +1,25 @@
function [rDZ] = getNLstepsize(ux,uy,gamma,dzmin,dzmax,dphimax,alpha_lin)
-
- maxPow = max(gamma.*max(real(ux).^2+imag(ux).^2+real(uy).^2+imag(uy).^2));
-
- Leff = dphimax/maxPow;
- alpha_lin = max([alpha_lin.X alpha_lin.Y]);
- nl_att_len_ratio = alpha_lin*Leff;
-
- if nl_att_len_ratio >= 1
- rDZ = dzmax;
+% gather() handles gpuArray inputs - ensures scalar is on CPU for comparisons
+maxPow = gather(max(gamma.*max(real(ux).^2+imag(ux).^2+real(uy).^2+imag(uy).^2)));
+
+Leff = dphimax/maxPow;
+alpha_lin = max([alpha_lin.X alpha_lin.Y]);
+nl_att_len_ratio = alpha_lin*Leff;
+
+if nl_att_len_ratio >= 1
+ rDZ = dzmax;
+else
+ if alpha_lin == 0
+ step = Leff;
else
- if alpha_lin == 0
- step = Leff;
- else
- %effective length?
- step = -1/alpha_lin*log(1-nl_att_len_ratio);
- end
-
- rDZ = min([step dzmax]);
- rDZ = max([rDZ dzmin]);
- end
-
+ %effective length?
+ step = -1/alpha_lin*log(1-nl_att_len_ratio);
+ end
+
+ rDZ = min([step dzmax]);
+ rDZ = max([rDZ dzmin]);
+end
+
end
\ No newline at end of file
diff --git a/Classes/02_optical/dp_fiber_lib/lin_step.m b/Classes/02_optical/dp_fiber_lib/lin_step.m
index eea8a1e..cdb3562 100644
--- a/Classes/02_optical/dp_fiber_lib/lin_step.m
+++ b/Classes/02_optical/dp_fiber_lib/lin_step.m
@@ -22,37 +22,37 @@ n_plates_left = n_plates - n_plates_done;
% compute last plate size ( if it fits, it should be 0)
if missing_dz > aStepSize
-
+
last_plate = aStepSize;
missing_dz = missing_dz-aStepSize;
plate_sizes = last_plate;
plate_numbers = n_plates;
-
+
else
-
+
last_plate = aStepSize - missing_dz - (n_plates_left-1)*corr_length;
-
+
if missing_dz == 0
missing_dz = [];
end
-
+
%build vector of plate lengths with missing plate part from prev.
%iterartion , then some normal plates and finally a fraction of a plate
%to fit into the step length
plate_sizes = [missing_dz corr_length*ones(1,n_plates_left-1) last_plate];
-
+
if n_plates_done == 0
plate_numbers =[(n_plates_done+1):(n_plates-1) n_plates];
else
plate_numbers = [n_plates_done (n_plates_done+1):(n_plates-1) n_plates]; % not wrking yet
end
-
- %remember for next step
+
+ %remember for next step
missing_dz = corr_length - last_plate;
-
+
end
-plate_steps = repmat(n_step,1,length(plate_sizes));
+plate_steps = repmat(n_step,1,length(plate_sizes)); %#ok<NASGU>
%
%figure;stem(plate_sizes);
@@ -66,50 +66,39 @@ test_plate_numbers = [test_plate_numbers, plate_numbers];
opt_x=fft(opt_x);
opt_y=fft(opt_y);
-% db1 = gpuArray(brf.db1);
-% db0 = gpuArray(brf.db0);
-% common_beta_x = gpuArray(common_beta_x);
-% common_beta_y = gpuArray(common_beta_y);
-
-db1 = (brf.db1);
-db0 = (brf.db0);
-common_beta_x = (common_beta_x);
-common_beta_y = (common_beta_y);
+% Note: db1, db0, common_beta_x, common_beta_y are already on GPU when
+% useGPU=true (transferred in CNLSE_plain)
+db1 = brf.db1;
+db0 = brf.db0;
% process every waveplate with given sizes in plate_sizes
for n=1:length(plate_sizes)
dz = plate_sizes(n);
-
- % figure(87);subplot(2,1,1);plot(real(x(900:1150)));subplot(2,1,2);plot(real(y(900:1150)));
- % MOV1=[MOV1 getframe(87)];
-
+
% extract rotation matrix from pre calculated matrices
matR = brf.matR{plate_numbers(n)};
-
+
% transform to eigenvalue of of fiber segment
tOpt.X = conj(matR(1,1))*opt_x + conj(matR(2,1))*opt_y;
tOpt.Y = conj(matR(1,2))*opt_x + conj(matR(2,2))*opt_y;
-
+
% calculate statistical delta beta for pmd
delta_beta = 0.5*(db1+db0(n))/corr_length;
- % build transfer function with delta beta
- %common.beta = beta1+beta2*omega^2
-
+
%accumulate delta beta for log...
brf.simdgd = brf.simdgd + (db1(length(db1)/2+1)+db0(n))/corr_length;
-
+
h.X = exp(-1j*(common_beta_x-delta_beta)*dz);
h.Y = exp(-1j*(common_beta_y+delta_beta)*dz);
- % delta_beta has to be added to the transfer function
-
+
% process with transfer function
tOpt.X = h.X.*tOpt.X ;
tOpt.Y = h.Y.*tOpt.Y ;
-
+
% rotate back
opt_x = matR(1,1)*tOpt.X + matR(1,2)*tOpt.Y;
opt_y = matR(2,1)*tOpt.X + matR(2,2)*tOpt.Y;
-
+
end
lin_z_test = lin_z_test + sum(plate_sizes,2);
@@ -117,9 +106,8 @@ lin_z_test = lin_z_test + sum(plate_sizes,2);
%update the number of processed plates so far
n_plates_done = n_plates_done + n_plates_left;
-% attanuate the signal each linear state with alpha
-% ( 0.2dB = 4.6052e-05 )
-rOpt_x=ifft(exp(-alpha_lin_x*aStepSize/2).*opt_x); % /2 not sure why (have to find it in formulas)
-rOpt_y=ifft(exp(-alpha_lin_y*aStepSize/2).*opt_y); % but not relevant for now
+% attenuate the signal each linear step with alpha
+rOpt_x=ifft(exp(-alpha_lin_x*aStepSize/2).*opt_x);
+rOpt_y=ifft(exp(-alpha_lin_y*aStepSize/2).*opt_y);
-end
\ No newline at end of file
+end
diff --git a/Classes/04_DSP/Equalizer/EQ.m b/Classes/04_DSP/Equalizer/EQ.m
index f9d58d2..a03d66b 100644
--- a/Classes/04_DSP/Equalizer/EQ.m
+++ b/Classes/04_DSP/Equalizer/EQ.m
@@ -10,10 +10,6 @@ classdef EQ < handle
training_length %Number of training symbols
training_loops %Number of loops through sequence for training mode
ideal_dfe %Error free DFE decisions
- weighted_DFE %Weighted DFE on/off
- weighted_DFE_mode %Weighted DFE mode
- weighted_DFE_d_min %d_min threshold parameter for weighted DFE mode 1
- weighted_DFE_I_mode %[a_s, b_s, I_max]-parameters for the weighted DFE
DB_aim %Aim at duobinary output sequence
@@ -72,10 +68,6 @@ classdef EQ < handle
options.training_length = 1024 %Number of training symbols
options.training_loops = 1 %Number of loops through sequence for training mode
options.ideal_dfe = 0 %Error free DFE decisions
- options.weighted_DFE = 0;
- options.weighted_DFE_mode = 'R1';
- options.weighted_DFE_d_min = 0.5;
- options.weighted_DFE_I_mode = [5,0.5,0.6];
options.DB_aim %Aim at duobinary output sequence
@@ -446,43 +438,6 @@ classdef EQ < handle
dd_out(k) = constellation_in_(dd_idx);
end
- % Implementation of a weighted DFE in
- % order to prevent error propagation.
- % For further details, study [1], chapter 3.2.2 -
- % Modifications of DFE
-
- if obj.weighted_DFE
- % define new constellations
- const = unique(ref_in);
-
- % determine reliability factor gamma_k
- if output_vec(m) > min(const) && output_vec(m) < max(const)
- gamma_k = 1 - abs(output_vec(m) - dd_out(k));
- else
- gamma_k = 1;
- end
-
- % select mode
- if strcmp(obj.weighted_DFE_mode,'R1')
- if gamma_k >= obj.weighted_DFE_d_min
- f_gamma_k = 1;
- else
- f_gamma_k = 0;
- end
- elseif strcmp(obj.weighted_DFE_mode,'R2')
- f_gamma_k = gamma_k;
- elseif strcmp(obj.weighted_DFE_mode,'I1')
- nom = 1-exp(-obj.weighted_DFE_I_mode(1)*((gamma_k/obj.weighted_DFE_I_mode(2))-1));
- denom = 1+exp(-obj.weighted_DFE_I_mode(1)*((gamma_k/obj.weighted_DFE_I_mode(2))-1));
- f_gamma_k = (1/2)*((nom/denom) - 1);
- elseif strcmp(obj.weighted_DFE_mode,'I2')
- nom = 1-exp(-obj.weighted_DFE_I_mode(1)*((gamma_k/obj.weighted_DFE_I_mode(2))-1));
- denom = 1+exp(-obj.weighted_DFE_I_mode(1)*((gamma_k/obj.weighted_DFE_I_mode(2))-1));
- f_gamma_k = (obj.weighted_DFE_I_mode(3)/2)*((nom/denom) - 1);
- end
- % calculate weighted output
- output_vec(m) = f_gamma_k.*dd_out(k)+(1-f_gamma_k).*output_vec(m);
- end
if obj.Nb(1) > 0
dd_DFE(2:end) = dd_DFE(1:end-1);
@@ -784,6 +739,3 @@ classdef EQ < handle
end
end
-% References
-% [1] T. J. Wettlin, “Experimental Evaluation of Advanced Digital Signal Processing for Intra-Datacenter Systems using Direct-Detection,” 2023. [Online]. Available: https://nbn-resolving.org/urn:nbn:de:gbv:8:3-2023-00703-8
-
diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m
index 9965525..168b837 100644
--- a/Classes/04_DSP/Equalizer/ML_MLSE.m
+++ b/Classes/04_DSP/Equalizer/ML_MLSE.m
@@ -597,7 +597,7 @@ classdef ML_MLSE < handle
obj.S = obj.nSym;
obj.Nf = obj.order * obj.sps;
obj.nStates = obj.S^obj.L;
- obj.nFeasible = obj.nStates * obj.S;
+ obj.nFeasible = obj.nStates * obj.S; %feasible state transitions
% --- Trellis mapping
obj.trellis_states = reshape(obj.constellation,1,[]);
diff --git a/Classes/04_DSP/Timing Recovery/Godard_Timing_Recovery.m b/Classes/04_DSP/Timing Recovery/Godard_Timing_Recovery.m
new file mode 100644
index 0000000..12eeeaf
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Godard_Timing_Recovery.m
@@ -0,0 +1,112 @@
+classdef Godard_Timing_Recovery < handle
+
+ properties(Access=public)
+ mode
+ num_blocks
+ fft_length
+ sps
+ rolloff
+ mu
+ Ki
+ end
+
+ methods(Access=public)
+ function obj = Godard_Timing_Recovery(options)
+ arguments(Input)
+
+ options.mode = 0;
+ options.num_blocks = 1;
+ options.fft_length = 1024;
+ options.sps = 2;
+ options.rolloff = 0;
+ options.mu = 0;
+ options.Ki = 1e-3;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out, tau_hat] = process(obj, data_in)
+
+ data_out = data_in;
+ output_vector = zeros(length(data_in),1);
+ block_length = length(data_in)/obj.num_blocks;
+ for i = 1:obj.num_blocks
+ i_start = ((i-1)*block_length)+1;
+ i_end = i*block_length;
+ x = data_in.signal(i_start:i_end);
+ beta = obj.rolloff;
+ eta = obj.sps;
+ R = fft(x,obj.fft_length);
+ R_full = fft(x);
+
+ if obj.mode == 0 % Classic Godard
+ % Calculate time shift
+ k0 = (1:obj.fft_length/2).';
+ idx1 = k0;
+ idx2 = k0+(obj.fft_length/2);
+ tau_hat = sum(imag(R(idx1) .* conj(R(idx2))));
+ elseif obj.mode == 1 || obj.mode == 2 % Modified Godard 1
+ % Calculate Shift for the received signal
+ shiftBins = (1 - 1/eta) * obj.fft_length;
+ if abs(shiftBins - round(shiftBins)) > 1e-12
+ disp('Warning: shiftBins=(1-1/eta)*fft_length is non-integer. Choose compatible values for fft_length and eta.');
+ end
+ shiftBins = round(shiftBins);
+
+ % Calculate upper and lower bounds
+ kStart = ((1-beta)/(2*eta)) * obj.fft_length;
+ kEnd = ((1+beta)/(2*eta)) * obj.fft_length - 1;
+ if abs(kStart - round(kStart)) > 1e-12 || abs(kEnd - round(kEnd)) > 1e-12
+ disp('Warning: kStart/kEnd are non-integer. Choose compatible values for fft_length, eta, and beta.');
+ end
+ kStart = round(kStart);
+ kEnd = round(kEnd);
+
+ k0 = (kStart:kEnd).';
+ idx1 = k0 + 1;
+ idx2 = mod(k0 + shiftBins, obj.fft_length) + 1;
+
+ % Calculate time shift
+ if obj.mode == 1
+ tau_hat = sum(imag(R(idx1) .* conj(R(idx2))));
+ elseif obj.mode == 2
+ tau_hat = sum(angle(R(idx1))-angle(R(idx2)));
+ end
+ end
+
+
+ % % Normalization
+ % denom = floor(log10(abs(tau_hat)));
+ % tau_hat = tau_hat / 10^denom;
+
+ % Calculate mu using a first-order loop filter
+ mu_block = obj.mu + obj.Ki * tau_hat;
+
+ % % Shifting the signal in time domain using interpolation
+ % x_original = linspace(0,length(x)-1,length(x)).';
+ % x_new = x_original + mu_block;
+ % output_vector(i_start:i_end) = interp1(x_original,x,x_new,'linear','extrap');
+
+ % Shifting the signal in frequency domain
+ k = (0:block_length-1).';
+ phaseRamp = exp(-1j * 2*pi * (k/block_length) * mu_block);
+ R_shifted = R_full .* phaseRamp;
+
+ output_vector(i_start:i_end) = real(ifft(R_shifted));
+
+ end
+ data_out.signal = output_vector;
+ end
+
+
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/MaxVar_Timing_Recovery.m b/Classes/04_DSP/Timing Recovery/MaxVar_Timing_Recovery.m
new file mode 100644
index 0000000..d6517d5
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/MaxVar_Timing_Recovery.m
@@ -0,0 +1,111 @@
+classdef MaxVar_Timing_Recovery < handle
+
+ properties(Access=public)
+ mode
+ sps
+ fsym
+ fadc
+ num_tau
+ comp_signal
+ comp_mode
+ end
+
+ methods(Access=public)
+ function obj = MaxVar_Timing_Recovery(options)
+ arguments(Input)
+
+ options.mode = 0;
+ options.sps = 2;
+ options.fsym = 32e9;
+ options.fadc = 80e9;
+ options.num_tau = 64;
+ options.comp_signal = 0;
+ options.comp_mode = 0;
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out] = process(obj, data_in)
+
+ data_out = data_in;
+ x = data_in.signal;
+
+ if obj.comp_mode
+ our_signal = data_in;
+ their_signal = obj.comp_signal;
+ end
+
+ if obj.mode == 0
+ vars = zeros(obj.sps,1);
+ for phi = 1:obj.sps
+
+ % Test variance for different start samples
+ y_phi = x(phi:obj.sps:end);
+ vars(phi) = var(y_phi);
+
+ % Comparison to reference signal
+ if obj.comp_mode
+ our_signal.signal = x(phi:obj.sps:end);
+ our_signal.fs = 6e9;
+ our_signal.normalize("mode","rms").plot("displayname",['Our signal, ' num2str(vars(phi))],'fignum',1231+phi);
+ their_signal.normalize("mode","rms").plot("displayname",'Their signal','fignum',1231+phi);
+ end
+
+ end
+
+ % Choose signal configuration with the maximum variance
+ [~,phi_opt] = max(vars);
+
+ y = x(phi_opt:obj.sps:end).';
+
+ elseif obj.mode == 1
+ % Create a grid with different (sub)sample starting points
+ T = 1/obj.fsym;
+ t = (0:length(x)-1)/obj.fadc;
+ tauGrid = linspace(0, T, obj.num_tau);
+
+ % Interpolate the signal starting from every defined point
+ % and calculate the MMSE/variance
+ vars = zeros(size(tauGrid));
+ for i = 1:numel(tauGrid)
+ tau = tauGrid(i);
+ % tk = linspace(tau, t(end), length(x)/obj.sps);
+ tk = tau : T : t(end)+tau;
+ xk = interp1(t, x, tk, 'linear', 'extrap');
+ if obj.comp_mode % MMSE
+ e = xk.' - obj.comp_signal.signal;
+ vars(i) = mean(abs(e).^2);
+ else % Variance
+ vars(i) = var(xk, 1);
+ end
+ end
+
+ if obj.comp_mode % MMSE
+ [~,idx] = min(vars);
+ else % Variance
+ [~,idx] = max(vars);
+ end
+ tau_opt = tauGrid(idx);
+
+ % Choosing the signal at optimum
+ % tk = linspace(tau_opt, t(end), length(x)/obj.sps);
+ tk = tau_opt : T : t(end)+tau_opt;
+ y = interp1(t, x, tk, 'linear', 'extrap');
+ end
+ % if length(y) ~= length(x)/obj.sps
+ % y = [y 0];
+ % end
+ data_out.signal = y.';
+ end
+
+
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/Time_Shifter.m b/Classes/04_DSP/Timing Recovery/Time_Shifter.m
new file mode 100644
index 0000000..f08e3fe
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Time_Shifter.m
@@ -0,0 +1,39 @@
+classdef Time_Shifter < handle
+
+ properties(Access=public)
+ value
+ end
+
+ methods(Access=public)
+ function obj = Time_Shifter(options)
+ arguments(Input)
+
+ options.value = 0;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out, tau_hat] = process(obj, data_in)
+
+ data_out = data_in;
+ x = data_in.signal;
+ R = fft(x);
+ k = (0:length(R)-1).';
+ phaseRamp = exp(-1j * 2*pi * (k/length(R)) * obj.value);
+ R_shifted = R .* phaseRamp;
+ data_out.signal = real(ifft(R_shifted));
+
+ end
+
+
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/Timing_Recovery.m b/Classes/04_DSP/Timing Recovery/Timing_Recovery.m
new file mode 100644
index 0000000..3999ad7
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Timing_Recovery.m
@@ -0,0 +1,49 @@
+classdef Timing_Recovery < handle
+
+ properties(Access=public)
+ modulation
+ timing_error_detector
+ sps
+ damping_factor
+ normalized_loop_bandwidth
+ detector_gain
+ end
+
+ methods(Access=public)
+ function obj = Timing_Recovery(options)
+ arguments(Input)
+
+ options.modulation = 'PAM/PSK/QAM';
+ options.timing_error_detector = 'Gardner (non-data-aided)';
+ options.sps = 2;
+ options.damping_factor = 1.0;
+ options.normalized_loop_bandwidth = 0.01;
+ options.detector_gain = 2.7;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out, timing_error] = process(obj, data_in)
+
+ timing_synchronization = comm.SymbolSynchronizer( ...
+ "Modulation", obj.modulation,...
+ "TimingErrorDetector", obj.timing_error_detector, ...
+ "SamplesPerSymbol", obj.sps, ...
+ "DampingFactor", obj.damping_factor, ...
+ "NormalizedLoopBandwidth", obj.normalized_loop_bandwidth, ...
+ "DetectorGain", obj.detector_gain);
+ data_out = data_in;
+ [data_out.signal, timing_error] = timing_synchronization(data_in.signal);
+
+ end
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/Timing_Recovery_GPT.m b/Classes/04_DSP/Timing Recovery/Timing_Recovery_GPT.m
new file mode 100644
index 0000000..33d0509
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Timing_Recovery_GPT.m
@@ -0,0 +1,77 @@
+classdef Timing_Recovery_GPT < handle
+
+ properties(Access=public)
+ sps
+ muGrid
+ end
+
+ methods(Access=public)
+ function obj = Timing_Recovery_GPT(options)
+ arguments(Input)
+
+ options.sps = 2;
+ options.muGrid = 0;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out, mu_best, score] = process(obj, data_in)
+ %MAXVARTIMINGSYNC Choose sampling phase mu that maximizes variance of downsampled symbols.
+ %
+ % data_in : matched-filtered samples (complex or real), length N
+ % sps : samples per symbol (here typically 2)
+ % muGrid : candidate fractional offsets in [0,1)
+ %
+ % data_out : symbol-rate samples (length floor(N/sps))
+ % mu_best: chosen fractional offset
+ % score : variance score for each mu in muGrid
+
+ data_out = data_in;
+
+ x = data_in.signal(:);
+ N = length(x);
+ Ns = floor(N/obj.sps);
+
+ if nargin < 3 || isempty(obj.muGrid)
+ obj.muGrid = linspace(0, 0.99, 101); % 0..0.99 in ~0.01 steps
+ end
+
+ % Symbol indices (1-based sample positions)
+ n0 = 1; % start sample index
+ k = (0:Ns-1).';
+ tBase = n0 + k*obj.sps; % integer times (1, 1+sps, ...)
+
+ score = zeros(numel(obj.muGrid),1);
+
+ for m = 1:numel(obj.muGrid)
+ mu = obj.muGrid(m);
+ t = tBase + mu;
+
+ % Linear fractional sampling
+ y = interp1(1:N, x, t, 'linear', 'extrap');
+
+ % For PAM, maximize variance of real part (or abs if you prefer)
+ yr = real(y);
+ score(m) = var(yr, 1); % use population variance (normalization doesn't matter for argmax)
+ end
+
+ % Pick best mu
+ [~, idx] = max(score);
+ mu_best = obj.muGrid(idx);
+
+ % Resample with best mu
+ t = tBase + mu_best;
+ data_out.signal = interp1(1:N, x, t, 'linear', 'extrap');
+
+ end
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/Timing_Recovery_Move_It.m b/Classes/04_DSP/Timing Recovery/Timing_Recovery_Move_It.m
new file mode 100644
index 0000000..bbd0578
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Timing_Recovery_Move_It.m
@@ -0,0 +1,77 @@
+classdef Timing_Recovery_Move_It < handle
+
+ properties(Access=public)
+ f_sim
+ gamma
+ end
+
+ methods(Access=public)
+ function obj = Timing_Recovery_Move_It(options)
+ arguments(Input)
+
+ options.f_sim = 14e9;
+ options.gamma = 0.1;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function data_out = process(obj, data_in)
+
+ e = NaN(size(data_in.signal));
+ T = 1/obj.f_sim;
+ t = zeros(size(data_in.signal));
+ t(2) = T/2;
+ mu = zeros(size(data_in.signal));
+
+% n = 2;
+ for k = 3:2:length(data_in.signal)
+ data_in.signal(k-1) = data_in.signal(k-1)*(1-mu(k-2)) + data_in.signal(k)*mu(k-2);
+ data_in.signal(k) = data_in.signal(k)*(1-mu(k-1)) + data_in.signal(k+1)*mu(k-1);
+
+ % TED
+ e(k) = (data_in.signal(k-2)-data_in.signal(k))*data_in.signal(k-1);
+ e(k+1) = e(k);
+
+ % TED Mueller Mueller
+% e(k) = ref_in(n-1)*data_in.signal(k) - ref_in(n)*data_in.signal(k-2);
+% e(k+1) = e(k);
+% n = n+1;
+%
+ % interpolator control
+% t(k) = t(k-1) + T/2 + obj.gamma/2*e(k);
+% t(k+1) = t(k) + T/2 + obj.gamma/2*e(k+1);
+ t(k) = t(k-1) + T/2 + obj.gamma*e(k)*T/2;
+ t(k+1) = t(k) + T/2 + obj.gamma*e(k+1)*T/2;
+% t(k) = k*T/2 + obj.gamma*e(k)*T/2;
+% t(k+1) = k*T/2 + obj.gamma*e(k+1)*T/2;
+
+ % interpolator
+ mu(k) = t(k)/(T/2) - round(t(k)/(T/2));
+ mu(k+1) = t(k+1)/(T/2) - round(t(k+1)/(T/2));
+
+ thres = 0.7;
+ if mu(k)-mu(k-1) > thres
+ mu(k) = mu(k) - 1;
+ elseif mu(k) - mu(k-1) < -thres
+ mu(k) = mu(k) + 1;
+ end
+
+ if mu(k+1)-mu(k) > thres
+ mu(k+1) = mu(k+1) - 1;
+ elseif mu(k+1) - mu(k) < -thres
+ mu(k+1) = mu(k+1) + 1;
+ end
+ end
+ data_out = data_in;
+ end
+ end
+end
+
diff --git a/Classes/04_DSP/Timing_Recovery.m b/Classes/04_DSP/Timing_Recovery.m
new file mode 100644
index 0000000..23cc331
--- /dev/null
+++ b/Classes/04_DSP/Timing_Recovery.m
@@ -0,0 +1,47 @@
+classdef Timing_Recovery < handle
+
+ properties(Access=public)
+ timing_error_detector
+ sps
+ damping_factor
+ normalized_loop_bandwidth
+ detector_gain
+ end
+
+ methods(Access=public)
+ function obj = Timing_Recovery(options)
+ arguments(Input)
+
+ options.timing_error_detector = 'Gardner';
+ options.sps = 2;
+ options.damping_factor = 1.0;
+ options.normalized_loop_bandwidth = 0.005;
+ options.detector_gain = 1;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+
+
+ end
+
+ function [data_out,timing_error] = process(obj, data_in)
+
+ timing_synchronization = comm.SymbolSynchronizer( ...
+ "TimingErrorDetector", obj.timing_error_detector, ...
+ "SamplesPerSymbol", obj.sps, ...
+ "DampingFactor", obj.damping_factor, ...
+ "NormalizedLoopBandwidth", obj.normalized_loop_bandwidth, ...
+ "DetectorGain", obj.detector_gain);
+
+ data_out = data_in;
+ [data_out.signal,timing_error] = timing_synchronization(data_in.signal);
+
+ end
+ end
+end
+
diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m
index 5569746..b7ba026 100644
--- a/Classes/DataBaseHandler/DBHandler.m
+++ b/Classes/DataBaseHandler/DBHandler.m
@@ -23,6 +23,12 @@ classdef DBHandler < handle
arguments
options.dataBase = ""; % Default value for pathToDB if not provided
options.type = "mysql";
+ options.server = "";
+ options.port = 3306;
+ options.user = "";
+ options.password = "";
+
+
end
% Assign values to class properties based on input arguments
@@ -46,12 +52,13 @@ classdef DBHandler < handle
obj.conn = database( ...
string(obj.dataBase), ... % Database name
- "silas", ... % Username
- "silas", ... % Password (or getSecret)
+ options.user, ... % Username
+ options.password, ... % Password (or getSecret)
"Vendor", "MySQL", ...
- "Server", "134.245.243.254", ...
+ "Server", options.server, ...
"PortNumber", 3306, ...
"JDBCDriverLocation", "C:\Users\Silas\Documents\mysql-connector-j-9.3.0\mysql-connector-j-9.3.0.jar");
+
end
catch e
diff --git a/Functions/EQ_structures/ml_mlse.m b/Functions/EQ_structures/ml_mlse.m
index 84ff5cb..04fe317 100644
--- a/Functions/EQ_structures/ml_mlse.m
+++ b/Functions/EQ_structures/ml_mlse.m
@@ -54,12 +54,6 @@ for k = 1:numel(fn)
end
json_str = jsonencode(eq_small);
-fn = fieldnames(eq_);
-for k = 1:numel(fn)
- if issparse(eq_.(fn{k}))
- eq_.(fn{k}) = full(eq_.(fn{k}));
- end
-end
ml_mlse_results.config.eq = jsonencode(eq_);
ml_mlse_results.config.equalizer_structure = int32(equalizer_structure.ml_mlse);
ml_mlse_results.config.comment = 'function: ML-based MLSE';
@@ -93,7 +87,6 @@ end
function [bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, precode_mode, M, eth_style)
% Calculate BER based on precoding mode
mapper = PAMmapper(M, 0, "eth_style", eth_style);
-skip_front = 150;
switch precode_mode
case db_mode.no_db
@@ -108,11 +101,11 @@ switch precode_mode
tx_bits_precoded = mapper.demap(tx_symbols_precoded);
rx_bits = mapper.demap(eq_signal_hd_precoded);
- [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", skip_front, "skip_end", 150, "returnErrorLocation", 1);
+ [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
% B) Just determine BER
rx_bits = mapper.demap(eq_signal_hd);
- [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", skip_front, "skip_end", 150, "returnErrorLocation", 1);
+ [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
case db_mode.db_precoded
% Data is precoded on TX side
@@ -120,12 +113,12 @@ switch precode_mode
eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd, "M", M);
eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded, "M", M);
rx_bits_decoded = mapper.demap(eq_signal_hd_decoded);
- [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", skip_front, "skip_end", 150, "returnErrorLocation", 1);
+ [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
% B) Omit the Coding by comparing with demapped TX symbol sequence
tx_bits_demapped = mapper.demap(tx_symbols);
rx_bits = mapper.demap(eq_signal_hd);
- [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", skip_front, "skip_end", 150, "returnErrorLocation", 1);
+ [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
end
end
diff --git a/Functions/Theory/Dissertation/mach_zehnder_modulator.m b/Functions/Theory/Dissertation/mach_zehnder_modulator.m
new file mode 100644
index 0000000..f9eca58
--- /dev/null
+++ b/Functions/Theory/Dissertation/mach_zehnder_modulator.m
@@ -0,0 +1,219 @@
+
+
+% Parameters
+c0 = physconst('lightspeed'); % [m/s]
+lambda0 = 1310e-9; % [m]
+omega0 = 2*pi*c0/lambda0;
+
+L = 5e-3; % [m] effective phase section length (set as needed)
+n_eff = 2.2; % [-] effective index (set as needed)
+
+E0 = 1; % field amplitude (arbitrary)
+Vpi = 3.2; % [V] half-wave voltage (your V_pi)
+
+% Drive
+f0 = 1e9; % [Hz]
+fs = 200e9; % [Hz]
+Nper = 2; % number of periods
+Vpp = 0.6*Vpi; % [V] peak-to-peak of v_drive(t)
+
+biasV = 1.1; % [V] differential bias added to v_drive
+
+% Time axis + differential drive voltage v_drive(t)
+T = Nper/f0;
+t = (0:1/fs:T-1/fs).';
+
+
+if 1
+ % SINE
+ v_drive = biasV + (Vpp/2)*sin(2*pi*f0*t); % v_drive(t) (peak = Vpp/2)
+
+else
+
+ % --- Generate PAM-4 Sequence ---
+ symbols = linspace(-0.5, 0.5, 4);
+ num_symbols = 12; % Increased slightly for better visual
+ rng(44);
+ random_data = symbols(randi(4, 1, num_symbols));
+
+ % Create time axis (Note: T is your period from the sine code)
+ sps = round(T * fs);
+ t = (0:1/fs:(num_symbols*T)-1/fs).';
+
+ % Upsample to rectangular waveform
+ v_pam = repelem(random_data, sps).';
+
+ % Apply swing and bias: Resulting range is [biasV-Vpp/2, biasV+Vpp/2]
+ v_drive_rect = biasV + (v_pam * Vpp);
+
+ % --- Round the edges ---
+ filter_span = round(sps/1.5); % Increased span for smoother "rounding"
+ window = gausswin(filter_span);
+ window = window / sum(window);
+
+ % Apply filter (using 'same' to keep vector length, but be aware of edge transients)
+ v_drive = conv(v_drive_rect, window, 'same');
+
+end
+
+
+% Analytic
+v_ = linspace(-1,2, 2001);
+% Field transfer function (amplitude)
+Field_mzm_analytic = cos((pi/2)*v_);
+
+% Power transfer function (intensity)
+P_mzm_analytic = Field_mzm_analytic.^2;
+
+% Imbalance factor in YOUR notation:
+rho = 1;
+
+% Push-pull branch voltages (consistent with v_drive = v1 - v2)
+v1 = +0.5*v_drive; % arm 1
+v2 = -0.5*v_drive; % arm 2
+
+% Phases phi1, phi2
+phi1 = pi * v1 / Vpi;
+phi2 = pi * v2 / Vpi;
+
+% Fields: E_in and E_out (exactly your Eq. (mzm_e_field))
+E_in = E0 .* exp(1i*omega0*t);
+
+common_phase = exp(-1i * (omega0*L*n_eff/c0)); % exp(-j*omega0*L*n_eff/c0)
+
+E_out = E0 .* exp(1i*omega0*t) .* common_phase .* 0.5 .* ...
+ ( exp(-1i*phi1) + rho .* exp(-1i*phi2) );
+
+% Transfer function (numerical): E_out/E_in
+H_num = E_out ./ E_in;
+
+% Power (normalized)
+Pnorm_num = abs(H_num).^2; % since |E_out/E_in|^2
+
+% Ideal TF (analytic) for comparison (rho=1, push-pull)
+H_ideal = common_phase .* cos( (pi/2) * (v_drive./Vpi) );
+
+Pnorm_ideal = abs(H_ideal).^2;
+Pnorm_math = cos( (pi/2) * (v_drive./Vpi) ).^2;
+
+
+set(groot, 'defaultLegendInterpreter', 'tex');
+set(groot, 'defaultAxesTickLabelInterpreter', 'tex');
+set(groot, 'defaultTextInterpreter', 'tex');
+
+% Normalized voltage axis (multiples of Vpi)
+v_norm = v_drive./Vpi;
+
+colfield = [0,0,0]; %is black
+colpow = linspecer(2);
+colpow = colpow(1,:);
+colvdrive = linspecer(2);
+colvdrive = colvdrive(2,:);
+
+%% SIGNAL IN
+figure(1); clf
+plot(v_norm,t*1e9, 'LineWidth', 1.0,'Color',colvdrive); grid on;
+ylabel('t [ns]'); xlabel('v_{drive}(t)/V_\pi');
+title('Drive voltage (normalized)');
+xlim([min(v_) max(v_)]);
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\linear_casee\mzm_input_signal.tex');
+
+
+%% IN/OUT (static transfer) — normalized x-axis + analytic curve
+if 0
+figure(2); clf
+plot(v_, Field_mzm_analytic, 'LineWidth', 1.2,'LineStyle','--','Color',colfield); hold on;% analytic power TF
+plot(v_, P_mzm_analytic, 'LineWidth', 1.2, 'Color',colpow); hold on;% analytic power TF
+% show input time signal
+plot(v_norm,-1+t*1e9, 'LineWidth', 1.0,'Color',colvdrive); grid on;
+% show output time signal
+plot(2+t*1e9, Pnorm_num, 'LineWidth', 1.0,'DisplayName','Intensity', 'Color',colvdrive); hold on;
+plot(2+t*1e9, real(H_ideal), '--', 'LineWidth', 1.0,'DisplayName','Field','Color',colfield); hold on;
+scatter(v_norm, Pnorm_num, 12, '.', 'LineWidth', 1,'MarkerEdgeColor',colvdrive);
+scatter(biasV./Vpi,(cos((pi/2)*biasV./Vpi)^2),10,'Marker','o');
+line([min(v_drive), min(v_drive)]./Vpi,[(cos((pi/2)*min(v_drive)./Vpi)^2), -2],'linewidth',0.5,'color','black','linestyle','--');
+line([max(v_drive) max(v_drive)]./Vpi,[(cos((pi/2)*max(v_drive)./Vpi)^2), -2],'linewidth',0.5,'color','black','linestyle','--');
+xline([min(v_norm) max(v_norm)])
+
+grid on;
+xlabel('v_{drive}(t)/V_\pi'); ylabel('|E_{out}/E_{in}|^2');
+% legend
+xlim([min(v_) max(v_)+1]);
+ylim([-1 1]);
+
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\mzm.tex');
+end
+%%
+
+figure(3); clf
+plot(v_, Field_mzm_analytic, 'LineWidth', 1.2,'LineStyle','--','Color',colfield); hold on;% analytic power TF
+plot(v_, P_mzm_analytic, 'LineWidth', 1.2, 'Color',colpow); hold on;% analytic power TF
+
+scatter(v_norm, Pnorm_num, 12, '.', 'LineWidth', 1,'MarkerEdgeColor',colvdrive);
+scatter(biasV./Vpi,(cos((pi/2)*biasV./Vpi)^2),10,'Marker','o');
+line([min(v_drive), min(v_drive)]./Vpi,[(cos((pi/2)*min(v_drive)./Vpi)^2), -2],'linewidth',0.5,'color','black','linestyle','--');
+line([max(v_drive) max(v_drive)]./Vpi,[(cos((pi/2)*max(v_drive)./Vpi)^2), -2],'linewidth',0.5,'color','black','linestyle','--');
+xline([min(v_norm) max(v_norm)])
+
+grid on;
+xlabel('v_{drive}(t)/V_\pi'); ylabel('|E_{out}/E_{in}|^2');
+% legend
+xlim([min(v_) max(v_)]);
+ylim([-1 1]);
+
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\mzm_tramsfer_function_matlab.tex');
+
+
+%%
+
+figure(4); clf
+% plot(v_, Field_mzm_analytic, 'LineWidth', 1.2,'LineStyle','--','Color',colfield); hold on;% analytic power TF
+plot(v_, P_mzm_analytic, 'LineWidth', 1.2, 'Color','black'); hold on;% analytic power TF
+input_dots = linspace(min(v_drive),max(v_drive),4)./Vpi;
+% input_dots = unique(v_drive_rect)./Vpi;
+output_dots = (cos((pi/2)*input_dots).^2);
+scatter(input_dots,output_dots,'Marker','x','LineWidth',1,'MarkerEdgeColor','black');
+scatter(input_dots,zeros(size(input_dots)),'Marker','^','LineWidth',2,'MarkerEdgeColor','black');
+% scatter(ones(size(input_dots)),output_dots,'Marker','<','LineWidth',2,'MarkerEdgeColor','black');
+
+for i = 1:numel(input_dots)
+ % Draw the dashed projection lines
+ line([input_dots(i), input_dots(i)], [output_dots(i), 0], 'linewidth', 0.5, 'color', 'black', 'linestyle', '--', 'handlevisibility', 'off');
+ line([input_dots(i), 1], [output_dots(i), output_dots(i)], 'linewidth', 0.5, 'color', 'black', 'linestyle', '--', 'handlevisibility', 'off');
+
+ % Add the level annotation boxes near the output (y-axis)
+ % Adjust the '1.05' to move the box further right or 'output_dots(i)' for height
+ j = 3-(i-1)*2;
+ text(1, output_dots(i), sprintf('Level %d', j), ...
+ 'FontSize', 8, ...
+ 'EdgeColor', 'black', ...
+ 'BackgroundColor', 'white', ...
+ 'Margin', 2);
+end
+
+xlim([0,1.5]);
+ylim([0,1])
+
+% line([min(v_drive), min(v_drive)]./Vpi,[(cos((pi/2)*min(v_drive)./Vpi)^2), 0],'linewidth',0.5,'color','black','linestyle','--');
+% line([max(v_drive), max(v_drive)]./Vpi,[(cos((pi/2)*max(v_drive)./Vpi)^2), 0],'linewidth',0.5,'color','black','linestyle','--');
+
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\linear_casee\mzm_tf.tex');
+% xticks(sort(input_dots));
+% yticks(sort(output_dots));
+grid off
+
+
+%%
+% % FIELD TF (only field here; do not mix power into this figure)
+figure(5); clf
+% plot(t*1e9, real(H_num), 'LineWidth', 1.0); hold on;
+% plot(t*1e9, real(H_ideal), '--', 'LineWidth', 1.0,'DisplayName','Field','Color',colfield); hold on;
+plot(t*1e9, Pnorm_num, 'LineWidth', 1.0,'DisplayName','Intensity', 'Color',colpow); hold on;
+grid on;
+xlabel('t [ns]'); ylabel('Re\{E_{out}/E_{in}\}');
+legend
+yticks(sort(output_dots));
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\linear_casee\mzm_output_signal.tex');
+
+
+
diff --git a/Functions/mat2tikz_improved.m b/Functions/mat2tikz_improved.m
new file mode 100644
index 0000000..a36b1d1
--- /dev/null
+++ b/Functions/mat2tikz_improved.m
@@ -0,0 +1,23 @@
+function mat2tikz_improved(filename)
+arguments
+ % Default to the path in your example if no argument is provided
+ filename (1,1) string = 'C:\Users\Silas\Documents\Dissertation\00_Examples\tikz\textfig.tikz';
+end
+cleanfigure;
+matlab2tikz(char(filename), ...
+ 'width','\fwidth', ...
+ 'height','\fheight', ...
+ 'showInfo',false, ...
+ 'extraAxisOptions',{ ...
+ 'legend style={font=\footnotesize}', ...
+ 'xlabel style={font=\color{white!15!black},font=\small},',...
+ 'ylabel style={font=\color{white!15!black},font=\small},',...
+ 'legend columns=1', ...
+ 'every axis/.append style={font=\scriptsize}',...
+ 'legend columns=1',...
+ 'legend style={at={(0.02,0.98)},font=\footnotesize,draw=black!60,rounded corners=2pt,inner sep=1pt,fill=white,column sep=6pt,anchor= north west}',...
+ 'legend style={at={(0.02,0.98)},draw=white!0!white,font=\scriptsize,inner sep=0.1pt,fill=white,column sep=1pt,anchor= north west}',...
+ 'every axis/.append style={font=\scriptsize}',...
+ });
+
+end
\ No newline at end of file
diff --git a/Libs/wesanderson_colors/WesPalette.m b/Libs/wesanderson_colors/WesPalette.m
index 7cd1dcb..13e976f 100644
--- a/Libs/wesanderson_colors/WesPalette.m
+++ b/Libs/wesanderson_colors/WesPalette.m
@@ -1,10 +1,13 @@
classdef WesPalette
% WESPALETTE Wes Anderson color palettes with auto-completion
% Usage:
- % cmap = WesPalette.Zissou1.rgb()
- % cmap = WesPalette.Zissou1.rgb(3)
-
- % https://github.com/karthik/wesanderson?tab=readme-ov-file
+ % cmap = WesPalette.Zissou1.rgb() % full palette
+ % cmap = WesPalette.Zissou1.rgb(3) % 3 colors (discrete default)
+ % cmap = WesPalette.Zissou1.rgb(12,"discrete") % any n, no interpolation
+ % cmap = WesPalette.Zissou1.rgb(256,"continuous") % smooth colormap (Lab interpolation)
+ %
+ % Requires:
+ % - colorspace.m (Pascal Getreuer) on MATLAB path for "continuous" mode
enumeration
BottleRocket1
@@ -34,21 +37,33 @@ classdef WesPalette
end
methods
- function cmap = rgb(obj, n)
+ function cmap = rgb(obj, n, mode)
% Return palette as Nx3 RGB colormap [0–1]
+ %
+ % n : number of requested colors (optional)
+ % mode : "discrete" (default) or "continuous"
- hex = obj.hex();
+ base_hex = obj.hex();
+ base_rgb = WesPalette.hex2rgb(base_hex);
- rgb = hex2rgb(hex);
+ if nargin < 2 || isempty(n)
+ cmap = base_rgb;
+ return;
+ end
+ if nargin < 3 || isempty(mode)
+ mode = "discrete";
+ end
+ mode = lower(string(mode));
- if nargin == 2
- if n > size(rgb,1)
- error('Requested %d colors, but only %d available.', ...
- n, size(rgb,1))
- end
- cmap = rgb(1:n,:);
+ validateattributes(n, {'numeric'}, {'scalar','integer','positive'}, mfilename, 'n');
+ if mode ~= "discrete" && mode ~= "continuous"
+ error('mode must be "discrete" or "continuous".');
+ end
+
+ if mode == "discrete"
+ cmap = WesPalette.sample_discrete(base_rgb, n);
else
- cmap = rgb;
+ cmap = WesPalette.interpolate_continuous_lab(base_rgb, n);
end
end
end
@@ -56,78 +71,116 @@ classdef WesPalette
methods (Access = private)
function hex = hex(obj)
% Internal HEX storage
-
switch obj
case WesPalette.BottleRocket1
hex = {'#A42820','#5F5647','#9B110E','#3F5151','#4E2A1E','#550307','#0C1707'};
-
case WesPalette.BottleRocket2
hex = {'#FAD510','#CB2314','#273046','#354823','#1E1E1E'};
-
case {WesPalette.Rushmore1, WesPalette.Rushmore}
hex = {'#E1BD6D','#EABE94','#0B775E','#35274A','#F2300F'};
-
case WesPalette.Royal1
hex = {'#899DA4','#C93312','#FAEFD1','#DC863B'};
-
case WesPalette.Royal2
hex = {'#9A8822','#F5CDB4','#F8AFA8','#FDDDA0','#74A089'};
-
case WesPalette.Zissou1
hex = {'#3B9AB2','#78B7C5','#EBCC2A','#E1AF00','#F21A00'};
-
case WesPalette.Zissou1Continuous
hex = {'#3A9AB2','#6FB2C1','#91BAB6','#A5C2A3','#BDC881', ...
'#DCCB4E','#E3B710','#E79805','#EC7A05','#EF5703','#F11B00'};
-
case WesPalette.Darjeeling1
hex = {'#FF0000','#00A08A','#F2AD00','#F98400','#5BBCD6'};
-
case WesPalette.Darjeeling2
hex = {'#ECCBAE','#046C9A','#D69C4E','#ABDDDE','#000000'};
-
case WesPalette.Chevalier1
hex = {'#446455','#FDD262','#D3DDDC','#C7B19C'};
-
case WesPalette.FantasticFox1
hex = {'#DD8D29','#E2D200','#46ACC8','#E58601','#B40F20'};
-
case WesPalette.Moonrise1
hex = {'#F3DF6C','#CEAB07','#D5D5D3','#24281A'};
-
case WesPalette.Moonrise2
hex = {'#798E87','#C27D38','#CCC591','#29211F'};
-
case WesPalette.Moonrise3
hex = {'#85D4E3','#F4B5BD','#9C964A','#CDC08C','#FAD77B'};
-
case WesPalette.Cavalcanti1
hex = {'#D8B70A','#02401B','#A2A475','#81A88D','#972D15'};
-
case WesPalette.GrandBudapest1
hex = {'#F1BB7B','#FD6467','#5B1A18','#D67236'};
-
case WesPalette.GrandBudapest2
hex = {'#E6A0C4','#C6CDF7','#D8A499','#7294D4'};
-
case WesPalette.IsleofDogs1
hex = {'#9986A5','#79402E','#CCBA72','#0F0D0E','#D9D0D3','#8D8680'};
-
case WesPalette.IsleofDogs2
hex = {'#EAD3BF','#AA9486','#B6854D','#39312F','#1C1718'};
-
case WesPalette.FrenchDispatch
hex = {'#90D4CC','#BD3027','#B0AFA2','#7FC0C6','#9D9C85'};
-
case WesPalette.AsteroidCity1
hex = {'#0A9F9D','#CEB175','#E54E21','#6C8645','#C18748'};
-
case WesPalette.AsteroidCity2
hex = {'#C52E19','#AC9765','#54D8B1','#B67C3B','#175149','#AF4E24'};
-
case WesPalette.AsteroidCity3
hex = {'#FBA72A','#D3D4D8','#CB7A5C','#5785C1'};
end
end
end
+
+ methods (Static, Access = private)
+ function rgb = hex2rgb(hex)
+ % hex: cellstr like {'#RRGGBB', ...}
+ if isstring(hex), hex = cellstr(hex); end
+ n = numel(hex);
+ rgb = zeros(n,3);
+ for i = 1:n
+ h = char(hex{i});
+ if startsWith(h,'#'), h = h(2:end); end
+ if numel(h) ~= 6
+ error('Invalid HEX color: %s', hex{i});
+ end
+ rgb(i,1) = hex2dec(h(1:2))/255;
+ rgb(i,2) = hex2dec(h(3:4))/255;
+ rgb(i,3) = hex2dec(h(5:6))/255;
+ end
+ end
+
+ function cmap = sample_discrete(base_rgb, n)
+ % No interpolation; allow any n by sampling/repeating.
+ k = size(base_rgb,1);
+
+ if n <= k
+ idx = round(linspace(1, k, n)); % spread across palette
+ idx = max(1, min(k, idx));
+ cmap = base_rgb(idx,:);
+ else
+ reps = floor(n / k);
+ rmd = mod(n, k);
+ cmap = [repmat(base_rgb, reps, 1); base_rgb(1:rmd,:)];
+ end
+ end
+
+ function cmap = interpolate_continuous_lab(base_rgb, n)
+ % Smooth interpolation in Lab using colorspace().
+ % Requires colorspace.m by Pascal Getreuer on MATLAB path.
+
+ k = size(base_rgb,1);
+ if k == 1
+ cmap = repmat(base_rgb, n, 1);
+ return;
+ end
+
+ % Convert to Lab, interpolate each channel, convert back
+ lab = colorspace('Lab<-RGB', base_rgb);
+
+ t_base = linspace(0, 1, k);
+ t_new = linspace(0, 1, n);
+
+ lab_new = zeros(n,3);
+ for c = 1:3
+ lab_new(:,c) = interp1(t_base, lab(:,c), t_new, 'linear');
+ end
+
+ rgb_new = colorspace('RGB<-Lab', lab_new);
+
+ % Clamp to displayable gamut
+ cmap = min(max(rgb_new, 0), 1);
+ end
+ end
end
diff --git a/Libs/wesanderson_colors/minimal_example_wespalette.m b/Libs/wesanderson_colors/minimal_example_wespalette.m
index 9fcd99b..f308f27 100644
--- a/Libs/wesanderson_colors/minimal_example_wespalette.m
+++ b/Libs/wesanderson_colors/minimal_example_wespalette.m
@@ -5,8 +5,8 @@ y2 = 1e0 ./ (1 + exp(-0.4*(x-12))); % NLPN
y3 = 1e-6 * 10.^(0.45*x); % RP on gamma
y4 = 1e-2 * 10.^(0.18*(x-8)); % RP on beta2
-cmap = WesPalette.AsteroidCity1.rgb(4);
-cmap = linspecer(4);
+cmap = WesPalette.AsteroidCity1;
+% cmap = linspecer(4);
figure1=figure(202998);clf;hold on
lw = 0.8; ms = 4;
plot(x,y1,'LineWidth',lw,'Color',cmap(1,:),'Marker','o','MarkerEdgeColor',cmap(1,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_FFE_DFE.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_FFE_DFE.m
new file mode 100644
index 0000000..29069ce
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_FFE_DFE.m
@@ -0,0 +1,36 @@
+taps_ffe = [200, 3, 2];
+taps_dfe = [5, 2, 1];
+trlen = 4096*2;
+trloops = 5;
+ddloops = 5;
+K_FFE = 1;
+DCmu = 0.005;
+mu_ffe_values = [0.001, 0.0005, 0.001];
+mu_dfe_values = 0.0007;
+DDmu = [mu_ffe_values, mu_dfe_values];
+DFEmu = 0.004;
+FFEmu = 0;
+plot_final = 0;
+idealdfe = 1;
+num_pf_coeffs = 1;
+damp_factor = 1;
+norm_loop_bw = 0.01;
+det_gain = 2.7;
+
+step_size = 0.0001;
+
+BER = [];
+taps_ffe_and_dfe = zeros(1,6);
+for i = 1:6
+ for j = 1:200
+ taps_ffe_and_dfe(i) = j;
+ BER_run = first_analysis_2(taps_ffe_and_dfe(1:3), taps_ffe_and_dfe(4:6), trlen, trloops, ddloops, ...
+ K_FFE, DCmu, DDmu, DFEmu, FFEmu, plot_final, idealdfe, num_pf_coeffs, ...
+ damp_factor, norm_loop_bw, det_gain);
+ BER = [BER, BER_run];
+ close all
+ end
+ [~,index_minimum_ber] = min(BER);
+ taps_ffe_and_dfe(i) = index_minimum_ber;
+end
+disp(taps_ffe_and_dfe)
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_ffe.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_ffe.m
new file mode 100644
index 0000000..8b55700
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_ffe.m
@@ -0,0 +1,57 @@
+%%
+ffe_start = 0;
+ffe_step = 5;
+ffe_end = 30;
+ffe_first_order = ffe_start:ffe_step:ffe_end;
+
+current = 225:10:285;
+current = string(current);
+power = [28.02, 33.2, 37.5, 42.3, 46.3, 49.3, 53.4];
+power = string(power);
+num_pf_coeff = 4;
+% taps_ffe = [300, 0, 0];
+taps_dfe = [0, 0, 0];
+M = 4;
+trlength = 4096*4;
+x = 225:10:285;
+BER_PAM_4 = [];
+
+for i = 1:length(ffe_first_order)
+ for eq_method = 2
+ for j = 4
+ BER_run = first_analysis_ber(current(j), power(j), num_pf_coeff, [300, 15, ffe_first_order(i)], taps_dfe, M, trlength, eq_method);
+ BER_PAM_4 = [BER_PAM_4, BER_run];
+ end
+ end
+end
+save('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_4_FFE_Second_Order_Tap_Sweep.mat', 'ffe_first_order', 'BER_PAM_4')
+BER_PAM_4 = [];
+
+%%
+BER = load('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_4_FFE_Second_Order_Tap_Sweep.mat');
+BER = BER.BER_PAM_4;
+
+figure(202120)
+plot(ffe_first_order, BER, '-o','LineWidth',1.75);
+hold on
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% FEC Labels direkt im Plot
+text(286,2.2e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(285,3.3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(282.5,5.4e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(287,2.4e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Number of DFE First Order Taps', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-4', 'Interpreter','latex')
+
+grid minor
+% ylim([1e-4 5e-1])
+set(gca,'YScale','log')
+% beautifyBERplot
+hold off
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_optimize_timing_recovery_parameters.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_optimize_timing_recovery_parameters.m
new file mode 100644
index 0000000..f867c5a
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_optimize_timing_recovery_parameters.m
@@ -0,0 +1,68 @@
+%% Fixe Simulationsparameter
+baud_rate = 6;
+power = 42.3;
+num_pf_coeff = 4;
+M = 4;
+trlength = 4096*2;
+
+%% === Suchräume für Tap-Werte (selbst bestimmbar) =================
+% WICHTIG: wähle Grenzen passend zur Skalierung deiner Taps.
+% Beispiel: FFE Taps vielleicht grob im Bereich [-500 .. 500],
+% DFE Taps eher kleiner, z.B. [-50 .. 50] – anpassen!
+ffeRange = [0, 500];
+dfeRange = [0, 200];
+
+vars = [
+ optimizableVariable('ffe1', ffeRange)
+ optimizableVariable('ffe2', ffeRange)
+ optimizableVariable('ffe3', ffeRange)
+
+ optimizableVariable('dfe1', dfeRange)
+ optimizableVariable('dfe2', dfeRange)
+ optimizableVariable('dfe3', dfeRange)
+];
+
+%% === Objective ===================================================
+objectiveFcn = @(x) objectiveWrapperTaps(x, ...
+ baud_rate, power, num_pf_coeff, M, trlength);
+
+%% === bayesopt ====================================================
+results = bayesopt(objectiveFcn, vars, ...
+ 'AcquisitionFunctionName','expected-improvement-plus', ...
+ 'MaxObjectiveEvaluations', 60, ...
+ 'IsObjectiveDeterministic', false, ...
+ 'ExplorationRatio', 0.5, ...
+ 'Verbose', 1, ...
+ 'PlotFcn', {@plotObjectiveModel,@plotMinObjective});
+
+%% === Beste Lösung ausgeben ======================================
+bestX = results.XAtMinObjective;
+bestBER = results.MinObjective;
+
+bestFFE = [bestX.ffe1, bestX.ffe2, bestX.ffe3];
+bestDFE = [bestX.dfe1, bestX.dfe2, bestX.dfe3];
+
+fprintf('\n=== Bestes Ergebnis ===\n');
+fprintf('BER : %.3e\n', bestBER);
+fprintf('taps_ffe : [%g %g %g]\n', bestFFE);
+fprintf('taps_dfe : [%g %g %g]\n\n', bestDFE);
+
+%% =================================================================
+function ber = objectiveWrapperTaps(x, baud_rate, power, num_pf_coeff, M, trlength)
+ taps_ffe = double([x.ffe1, x.ffe2, x.ffe3]);
+ taps_dfe = double([x.dfe1, x.dfe2, x.dfe3]);
+
+ % Optional: falls du z.B. Monotonicität / Struktur erzwingen willst, hier.
+ % taps_dfe(1) = 0; % Beispiel: ersten DFE-Tap fixieren
+
+ try
+ ber = first_analysis_optimize(baud_rate, power, num_pf_coeff, taps_ffe, taps_dfe, M, trlength);
+
+ if isempty(ber) || ~isscalar(ber) || ~isfinite(ber) || ber < 0
+ ber = 1; % Penalty
+ end
+ catch ME
+ warning("Objective failed: %s", ME.message);
+ ber = 1; % Penalty
+ end
+end
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_2.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_2.m
new file mode 100644
index 0000000..b0310bd
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_2.m
@@ -0,0 +1,66 @@
+%%
+current = 225:10:285;
+current = string(current);
+power = [28.02, 33.2, 37.5, 42.3, 46.3, 49.3, 53.4];
+power = string(power);
+num_pf_coeff = 4;
+taps_ffe = [200, 0, 0];
+taps_dfe = [0, 0, 0];
+M = 2;
+trlength = 4096*2;
+x = 225:10:285;
+BER_PAM_2 = [];
+post_only = 0;
+
+for eq_method = 2:4
+
+ if ~post_only
+ for j = 1:length(current)
+ BER_run = first_analysis_ber(current(j), power(j), num_pf_coeff, taps_ffe, taps_dfe, M, trlength, eq_method);
+ BER_PAM_2 = [BER_PAM_2, BER_run];
+ end
+
+ save('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_2_' + string(eq_method) + '.mat', 'x', 'BER_PAM_2')
+ end
+
+end
+
+%%
+x = 225:10:285;
+for k = 1:4
+ BER = load('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_2_' + string(k) + '.mat');
+ BER = BER.BER_PAM_2;
+
+ figure(202120)
+ plot(x, BER, '-o','LineWidth',1.75);
+ hold on
+end
+old_BER = [-1.5, -1.75, -2, -2.3, -2.6, -2.25, -1.95];
+old_BER = 10.^(old_BER);
+plot(x, old_BER, '-o','LineWidth',1.75)
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% Legende NUR für Kurven
+legend('FFE', 'FFE+PF+MLSE', 'DB', 'ML-MLSE', 'BER Paper', ...
+ 'Interpreter','latex', ...
+ 'Location','southwest', 'FontSize', 14)
+
+% FEC Labels direkt im Plot
+text(280,2.2e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(280,3.3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(280,5.4e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(280,2.4e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Laser Bias Current [mA]', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-2', 'Interpreter','latex')
+
+grid minor
+ylim([1e-4 5e-1])
+set(gca,'YScale','log')
+% beautifyBERplot
+hold off
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4.m
new file mode 100644
index 0000000..cf3b301
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4.m
@@ -0,0 +1,61 @@
+%%
+current = 225:10:285;
+current = string(current);
+power = [28.02, 33.2, 37.5, 42.3, 46.3, 49.3, 53.4];
+power = string(power);
+num_pf_coeff = 4;
+taps_ffe = [300, 0, 0];
+taps_dfe = [5, 0, 0];
+M = 4;
+trlength = 4096*4;
+x = 225:10:285;
+BER_PAM_4 = [];
+
+for eq_method = 2
+ for j = 4
+ BER_run = first_analysis_ber(current(j), power(j), num_pf_coeff, taps_ffe, taps_dfe, M, trlength, eq_method);
+ BER_PAM_4 = [BER_PAM_4, BER_run];
+ end
+ save('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_4_DFE_' + string(eq_method) + '.mat', 'x', 'BER_PAM_4')
+ BER_PAM_4 = [];
+end
+
+%%
+x = 225:10:285;
+for k = 1:4
+ BER = load('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_4_' + string(k) + '.mat');
+ BER = BER.BER_PAM_4;
+
+ figure(202120)
+ plot(x, BER, '-o','LineWidth',1.75);
+ hold on
+end
+old_BER = [-1.6, -1.85, -2.2, -2.45, -2.3, -2, -1.4];
+old_BER = 10.^(old_BER);
+plot(x, old_BER, '-o','LineWidth',1.75)
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% Legende NUR für Kurven
+legend('FFE', 'FFE+PF+MLSE', 'DB', 'ML-MLSE', 'BER Paper', ...
+ 'Interpreter','latex', ...
+ 'Location','southwest', 'FontSize', 14)
+
+% FEC Labels direkt im Plot
+text(286,2.2e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(285,3.3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(282.5,5.4e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(287,2.4e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Laser Bias Current [mA]', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-4', 'Interpreter','latex')
+
+grid minor
+ylim([1e-4 5e-1])
+set(gca,'YScale','log')
+% beautifyBERplot
+hold off
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4_baud_rate_sweep.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4_baud_rate_sweep.m
new file mode 100644
index 0000000..0575787
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4_baud_rate_sweep.m
@@ -0,0 +1,77 @@
+baud_rate = 4:1:8;
+baud_rate = string(baud_rate);
+power = [8.4, 8.4, 42.3, 8.4, 8.4];
+power = string(power);
+num_pf_coeff = 4;
+taps_ffe = [300, 0, 0];
+taps_dfe = [0, 0, 0];
+M = 4;
+trlength = 4096*4;
+x = 4:1:8;
+BER_PAM_4 = [];
+Alpha_PAM_4 = [];
+
+for method = 1:4
+ for i = 1:5
+ [BER_run, ~] = first_analysis_baud_rate_sweep(baud_rate(i), power(i), num_pf_coeff, taps_ffe, taps_dfe, M, trlength, method);
+ BER_PAM_4 = [BER_PAM_4, BER_run];
+ % Alpha_PAM_4 = [Alpha_PAM_4, Alpha_run];
+ end
+ save('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\New Baud Rate Sweep Data\BER_PAM_4_' + string(method) + '.mat', 'x', 'BER_PAM_4')
+ BER_PAM_4 = [];
+end
+
+%% BER
+for k = 1:4
+ BER = load('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\New Baud Rate Sweep Data\BER_PAM_4_' + string(k) + '.mat');
+ BER = BER.BER_PAM_4;
+ % x = BER.x;
+ % BER_Alpha = load(filename);
+ % BER = BER_Alpha.BER_PAM_4;
+
+ % close all
+ figure(239)
+ plot(x, BER, '-o','LineWidth',1.75)
+ hold on
+end
+
+% old_BER = [-4.4, -4, -3.3, -2.7, -2.5, -2.475];
+% old_BER = 10.^(old_BER);
+% plot(x, old_BER, '--o','LineWidth',1)
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% Legende NUR für Kurven
+legend('FFE','FFE+PF+MLSE','DB','ML-MLSE',...
+ 'Interpreter','latex', ...
+ 'Location','southwest', 'FontSize', 14)
+
+% FEC Labels direkt im Plot
+text(8.2,2.3e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(8.2,3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(8.2,5.6e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(8.2,2.5e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Symbol Rate [GBd]', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-4', 'Interpreter','latex')
+
+grid minor
+% ylim([1e-4 5e-2])
+xlim([3 9])
+set(gca,'YScale','log')
+hold off
+
+%% Channel Alpha
+% Alpha = BER_Alpha.Alpha_PAM_4;
+% figure
+% plot(x, Alpha, '--o','LineWidth',1)
+% hold on
+% xlabel('Symbol Rate [GBd]', 'Interpreter','latex')
+% ylabel('Channel Alpha', 'Interpreter','latex')
+% title('Channel Alpha for PAM-4 - 300-Tap-FFE - 1 Postfilter Coefficients - 8192 Training Symbols', 'Interpreter','latex')
+% grid minor
+% hold off
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_timing_shift.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_timing_shift.m
new file mode 100644
index 0000000..435b540
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_timing_shift.m
@@ -0,0 +1,9 @@
+time_shift = -2:0.1:2;
+BER = zeros(1,length(time_shift));
+for i = 1:length(time_shift)
+ BER_value = first_analysis_time_shift(time_shift(i));
+ BER(i) = BER_value;
+ close all
+end
+figure;
+plot(time_shift,BER)
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_2.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_2.m
new file mode 100644
index 0000000..c3a44f0
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_2.m
@@ -0,0 +1,190 @@
+function BER_value = first_analysis_2(taps_ffe, taps_dfe, trlen, trloops, ddloops, K_FFE, DCmu, DDmu, DFEmu, FFEmu, plot_final, idealdfe, num_pf_coeffs, damp_factor, norm_loop_bw, det_gain)
+ %%
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+ mode = 0; %0 oder 1
+ M = 2;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+
+ % Rx_Time_Rec = Rx_matched;
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2, ...
+ 'damping_factor',damp_factor,'normalized_loop_bandwidth',norm_loop_bw,'detector_gain',det_gain).process(Rx_matched_1);
+ figure;plot(Timing_Error);
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 28e9, 14e9);
+
+ %% not working..
+ Rx_synced = Rx_Time_Rec;
+ Rx_synced.fs = 14e9;
+ % Rx_synced = Rx_synced_cell{1};
+ % len_tr = trlen;
+ % mu_ffe1 = mu_ffe_values(1);
+ % mu_ffe2 = mu_ffe_values(2);
+ % mu_ffe3 = mu_ffe_values(3);
+ % mu_dc = 0.005;
+ % mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ % mu_dfe = mu_dfe_values;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',2);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',0);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',0);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',0);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ % "training_length",trlen,"training_loops",trloops,"dd_loops",ddloops, ...
+ % "K",K_FFE,"DCmu",DCmu,"DDmu",DDmu,"DFEmu",DFEmu, ...
+ % "FFEmu",FFEmu,"plotfinal",plot_final,"ideal_dfe",idealdfe);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % % ffe_results.metrics.print
+ % fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % BER_value = ffe_results.metrics.BER;
+
+ %% -------------------- VNLE + MLSE --------------------
+
+ pf_ncoeffs = num_pf_coeffs;
+ eq_v = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ "training_length",trlen,"training_loops",trloops,"dd_loops",ddloops, ...
+ "K",K_FFE,"DCmu",DCmu,"DDmu",DDmu,"DFEmu",DFEmu, ...
+ "FFEmu",FFEmu,"plotfinal",plot_final,"ideal_dfe",idealdfe);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = mlse_results.metrics.BER;
+
+ %% -------------------- DB target --------------------
+ % mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+ %
+ % eq_ = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ % "training_length",trlen,"training_loops",trloops,"dd_loops",ddloops, ...
+ % "K",K_FFE,"DCmu",DCmu,"DDmu",DDmu,"DFEmu",DFEmu, ...
+ % "FFEmu",FFEmu,"plotfinal",plot_final,"ideal_dfe",idealdfe);
+ %
+ % dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ % "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+ %
+ % dbt_results.metrics.print("description",'Duobinary');
+ % mlse_results.metrics.print
+ % fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced)/2,"mu_dd",0.03,"mu_tr",0.03,"order",30,"sps",1, ...
+ % "traceback_depth",256,"L",2,"delta",10,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % fprintf('ML-based MLSE:');
+ % fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ %
+ % % -------------------- Post-FFE --------------------
+ % eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",1e-4,"mu_tr",0,"order",1001,"sps",1,"decide",0);
+ % post_ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ % fprintf('Post-FFE:');
+ % fprintf('My EQ: %.1e \n',post_ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_baud_rate_sweep.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_baud_rate_sweep.m
new file mode 100644
index 0000000..0fe859c
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_baud_rate_sweep.m
@@ -0,0 +1,202 @@
+function [BER, Channel_Alpha] = first_analysis_baud_rate_sweep(baud_rate, power, num_pf_coeff, taps_ffe, taps_dfe, M, trlength, method)
+ %%
+ close all
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\Sweep Data\";
+ mode = 0; %0 oder 1
+ % M = 2;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=" + current + "mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, baud_rate + "G_PAM4\tx_info\tx_info_PAM4_" + baud_rate + "Gbd0.6RRC.mat");
+ filename = fullfile(base, baud_rate + "G_PAM4\M=4_Rs="+ baud_rate + "e9_Fs=8e10_I=255mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ Kov = 14;
+
+ Scope_sig = Scope_sig.resample('fs_in',fs,'fs_out',Kov*fsym);
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+
+ % Rx_Time_Rec = Rx_matched;
+ % Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+
+ Time_Rec = 1;
+ if Time_Rec
+ Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*128,'sps',Kov,'comp_signal',0,'comp_mode',0).process(Rx_matched_1);
+ sps = 1;
+ else
+ sps = 2;
+ end
+
+ Rx_Time_Rec.fs = fsym;
+
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 12e9, 6e9);
+
+ % Rx_matched_1.plot("fignum",231231)
+ % Rx_Time_Rec.plot("fignum",231231)
+
+ %% not working..
+ Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = trlength;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ if method == 1
+ %% -------------------- FFE --------------------
+ % requires some more digging what is going on :-)
+ eq_ffe = EQ("Ne",[500, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+ ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ "eth_style_symbol_mapping",mapping_style);
+
+ % ffe_results.metrics.print
+ fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = ffe_results.metrics.BER;
+
+ elseif method == 2
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = num_pf_coeff;
+ eq_v = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = mlse_results.metrics.BER;
+ % Channel_Alpha = mlse_results.metrics.Alpha;
+
+ elseif method == 3
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ ml_mlse_equalizer = ML_MLSE("epochs_tr",150,"epochs_dd",1, ...
+ "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",80,"sps",sps, ...
+ "traceback_depth",256,"L",1,"delta",4,"adaptive_mu",0);
+
+ [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ fprintf('ML-based MLSE:\n');
+ fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = ml_mlse_results.metrics.BER;
+
+ elseif method == 4
+ %% -------------------- DB target --------------------
+ mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+
+ eq_ = EQ("Ne",taps_ffe,"Nb",taps_dfe,"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+ dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+
+ dbt_results.metrics.print("description",'Duobinary');
+ fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = dbt_results.metrics.BER;
+ end
+ Channel_Alpha = 0;
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_ber.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_ber.m
new file mode 100644
index 0000000..bc02b58
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_ber.m
@@ -0,0 +1,207 @@
+function BER_value = first_analysis_ber(current, power, num_pf_coeff, taps_ffe, taps_dfe, M, trlength, eq_method)
+ %%
+ close all
+
+ %%
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+ mode = 0; %0 oder 1
+ % M = 2;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=" + current + "mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=" + current + "mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ Kov = 14;
+
+ Scope_sig = Scope_sig.resample('fs_in', fs, 'fs_out', Kov*fsym);
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+
+ % Rx_Time_Rec = Rx_matched;
+ % Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+
+ Time_Rec = 1;
+ if Time_Rec
+ Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*128,'sps',Kov,'comp_signal',0,'comp_mode',0).process(Rx_matched_1);
+ sps = 1;
+ else
+ sps = Kov;
+ end
+ Rx_Time_Rec.fs = fsym;
+
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 12e9, 6e9);
+
+ % Rx_matched_1.plot("fignum",231231)
+ % Rx_Time_Rec.plot("fignum",231231)
+
+ %% not working..
+ Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = trlength;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ if eq_method == 1
+
+ %% -------------------- FFE --------------------
+ % requires some more digging what is going on :-)
+ eq_ffe = EQ("Ne",[500, 0, 0],"Nb",[0,0,0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+ ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ "eth_style_symbol_mapping",mapping_style);
+
+ % ffe_results.metrics.print
+ fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = ffe_results.metrics.BER;
+
+ elseif eq_method == 2
+
+ %% -------------------- VNLE + MLSE --------------------
+
+ pf_ncoeffs = num_pf_coeff;
+ eq_v = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = mlse_results.metrics.BER;
+
+ elseif eq_method == 3
+
+ %% -------------------- DB target --------------------
+ mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+
+ eq_ = EQ("Ne",taps_ffe,"Nb",taps_dfe,"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+ dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+
+ dbt_results.metrics.print("description",'Duobinary');
+ fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = dbt_results.metrics.BER;
+
+ elseif eq_method == 4
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ ml_mlse_equalizer = ML_MLSE("epochs_tr",150,"epochs_dd",1, ...
+ "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",80,"sps",1, ...
+ "traceback_depth",256,"L",1,"delta",4,"adaptive_mu",0);
+
+ [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ fprintf('ML-based MLSE:\n');
+ fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = ml_mlse_results.metrics.BER;
+
+ end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_mf.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_mf.m
new file mode 100644
index 0000000..b2a6027
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_mf.m
@@ -0,0 +1,293 @@
+
+<<<<<<< HEAD
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+=======
+base = "C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC";
+>>>>>>> 719e5508e776c18e3dcf72a954c71bdedda179e0
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+<<<<<<< HEAD
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+=======
+ tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+>>>>>>> 719e5508e776c18e3dcf72a954c71bdedda179e0
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_tx_compare = Pform.process(Symbols);
+
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sync = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_tx_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+timesig_compare = [0:1:datas.tr.lastData(1).trace.ch3.Points-1] ./ fs;
+timesig = datas.tr.lastData(1).trace.ch3.XData;
+assert(isequal(traceData.YData,scoperead_volts));
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+%%
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef directly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+apply_matched_filter = 0;
+k = 4;
+if apply_matched_filter
+
+ Pform = Pulseformer("fsym",fsym,"fdac",k*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Pform.process(Scope_sig);
+else
+
+ Rx_matched = Filter('filtdegree',4,"f_cutoff",fsym*0.5,"fs",Scope_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scope_sig);
+ Rx_matched = Rx_matched.resample("fs_out",k*fsym);
+end
+Rx_matched.spectrum();
+
+<<<<<<< HEAD
+%%
+
+sys = comm.SymbolSynchronizer('TimingErrorDetector', 'Gardner (non-data-aided)', ...
+ 'SamplesPerSymbol', 2, ...
+ 'DampingFactor', 0.7, ...
+ 'NormalizedLoopBandwidth', 0.01);
+Rx_symbolsync = Rx_matched;
+[Rx_symbolsync.signal, timing_error] = sys(Rx_matched.signal);
+
+plot(timing_error); % If this is a ramp, you have drift!
+
+%% timing sync -> at this point we still have no symbol timing recovery, we
+% % try to do this with 2sps EQ!
+
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+% Rx_matched_1 = Rx_synced_cell{1};
+%
+% % Rx_Time_Rec = Rx_matched;
+% [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+% figure;plot(Timing_Error);
+% % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 28e9, 14e9);
+
+%% not working..
+% Rx_synced = Rx_Time_Rec;
+Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*2;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.005;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',2);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',0);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',0);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',0);
+=======
+%% Timing Rec
+apply_timing_rec = 1;
+if apply_timing_rec
+ [Rx_symbolsync, timing_error] = Timing_Recovery("timing_error_detector",'Gardner (non-data-aided)','sps',k,'damping_factor',0.1,'normalized_loop_bandwidth',0.1,'detector_gain',2.7).process(Rx_matched);
+ figure();plot(timing_error);
+ Rx_symbolsync.fs = fsym;
+ % Tsynch
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
+ Rx_synced = Rx_synced_cell{1};
+ sps = 1;
+else
+ % Tsynch
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
+ Rx_synced = Rx_synced_cell{1};
+ Rx_synced = Rx_synced.resample("fs_out",2*fsym);
+ sps = 2;
+end
+>>>>>>> 719e5508e776c18e3dcf72a954c71bdedda179e0
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+
+%%
+
+Rx_synced = Rx_synced_cell{1};
+
+
+% -------------------- FFE --------------------
+% requires some more digging what is going on :-)
+eq_ffe = EQ("Ne",[50, 1, 1],"Nb",[2,0,0], ...
+ "training_length",512,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+vars = logspace(-4,-3,36);
+
+parfor i = 1:numel(vars)
+
+ len_tr = 4096;
+ mu_ffe1 = 0.01;% mus(i);%0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.005;
+ mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
+ mu_dfe = vars(i);
+ duob_mode = db_mode.no_db;
+
+ % requires some more digging what is going on :-)
+ eq_ffe_1 = EQ("Ne",[150, 1, 0],"Nb",[50,0,0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",0);
+
+ eq_ffe_2 = FFE("epochs_tr",1,"epochs_dd",vars(i),"len_tr",4096,"mu_dd",vars(i),"mu_tr",vars(i),"order",999,"sps",1,"decide",0, "adaption",adaption_method.nlms,"dd_mode",0);
+ % eq_ffe_2 = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-5,"dfe_mu_dd",mus(i),"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",50,"dfe_order",10,"sps",1,"decide",1);
+
+
+ ffe_results = ffe(eq_ffe_1,M,Rx_synced,Symbols,Bits, ...
+ "precode_mode",duob_mode,'showAnalysis',1,"postFFE",[], ...
+ "eth_style_symbol_mapping",mapping_style);
+
+ ffe_results.metrics.BER
+ bers(i) = ffe_results.metrics.BER;
+end
+
+figure();
+plot(vars,bers);
+yline(ber_in_paper)
+beautifyBERplot();
+%
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+ffe_results.metrics.print("description",'FFE');
+fprintf('FFE: %.1e \n',ffe_results.metrics.BER);
+
+
+%% -------------------- VNLE + MLSE --------------------
+len_tr = 4096;
+mu_ffe1 = 0.0001;% mus(i);%0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.005;
+mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+
+pf_ncoeffs = 4;
+
+vars = 1:7;
+bers = zeros(size(vars));
+parfor i = 1:numel(vars)
+
+ eqv = EQ("Ne",[200, 1, 0],"Nb",[2, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ncoeffs = vars(i);
+ pf = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eqv, pf, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ vnle_results.metrics.print("description",'VNLE');
+ mlse_results.metrics.print("description",'MLSE');
+ bers(i) = mlse_results.metrics.BER;
+end
+%%
+figure();hold on
+plot(vars,bers_ffe,'DisplayName','FFE [200,0,0] + PF + MLSE');
+plot(vars,bers_vnle,'DisplayName','VNLE [200,1,0] + PF + MLSE');
+plot(vars,bers_vnledfe,'DisplayName','VNLE [200,1,0] + DFE [2] + PF + MLSE');
+plot(vars,bers_vnledfe_ideal,'DisplayName','VNLE [200,1,0] + ideal DFE [2] + PF + MLSE');
+yline(ber_in_paper);
+yline([2e-2, 4.85e-3, 3.8e-3, 2,2e-4],'LineWidth',2,'Color',[0.8,0.8,0.8],'LineStyle',':','HandleVisibility','off');
+ylim([1e-5,0.1]);
+beautifyBERplot();
+
+%% -------------------- DB target --------------------
+mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+
+eq_ = EQ("Ne",[50, 5, 5],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+dbt_results.metrics.print("description",'Duobinary');
+
+
+%%
+
+%ML-based MLSE (L=2)
+mu_ml = 0.01; training_epochs = 100;
+ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
+ "len_tr",len_tr,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",1, ...
+ "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0);
+
+[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode);
+ml_mlse_results.metrics.print("description",'ML ');
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_optimize.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_optimize.m
new file mode 100644
index 0000000..5d5b4f9
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_optimize.m
@@ -0,0 +1,177 @@
+function BER = first_analysis_optimize(baud_rate, power, num_pf_coeff, taps_ffe, taps_dfe, M, trlength)
+ %%
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\Sweep Data\";
+ mode = 0; %0 oder 1
+ % M = 2;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=" + current + "mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, baud_rate + "G_PAM4\tx_info\tx_info_PAM4_" + baud_rate + "Gbd0.6RRC.mat");
+ filename = fullfile(base, baud_rate + "G_PAM4\M=4_Rs="+ baud_rate + "e9_Fs=8e10_I=255mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+
+ % Rx_Time_Rec = Rx_matched;
+ % Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+
+ Time_Rec = 1;
+ if Time_Rec
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',0.0123793,'normalized_loop_bandwidth',4.50865e-06,'detector_gain',3.33311).process(Rx_matched_1);
+ sps = 1;
+ else
+ sps = 2;
+ end
+
+ baud_rate_num = str2double(baud_rate);
+ Rx_Time_Rec.fs = baud_rate_num*10^9;
+
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 12e9, 6e9);
+
+ % Rx_matched_1.plot("fignum",231231)
+ % Rx_Time_Rec.plot("fignum",231231)
+
+ %% not working..
+ Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = trlength;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[400, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % % ffe_results.metrics.print
+ % fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % BER_value = ffe_results.metrics.BER;
+
+ %% -------------------- VNLE + MLSE --------------------
+
+ pf_ncoeffs = num_pf_coeff;
+ eq_v = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = mlse_results.metrics.BER;
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",150,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",80,"sps",1, ...
+ % "traceback_depth",256,"L",1,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % fprintf('ML-based MLSE:\n');
+ % fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec.m
new file mode 100644
index 0000000..22622ec
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec.m
@@ -0,0 +1,160 @@
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 4;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+% Rx_Time_Rec = Rx_matched;
+% Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+[Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+
+%% not working..
+Rx_synced = Rx_Time_Rec;
+% Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*2;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.004;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+sps = 1;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+%% -------------------- FFE --------------------
+% % requires some more digging what is going on :-)
+% eq_ffe = EQ("Ne",[50, 5, 5],"Nb",[2,0,0], ...
+% "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+% "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+% "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+% "eth_style_symbol_mapping",mapping_style);
+%
+% % ffe_results.metrics.print
+% fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+% BER_value = ffe_results.metrics.BER;
+
+%% -------------------- VNLE + MLSE --------------------
+
+pf_ncoeffs = 1;
+eq_v = EQ("Ne",[200, 3, 2],"Nb",[5, 2, 1], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+mlse_results.metrics.print
+fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- ML-based MLSE (L=2) --------------------
+% ml_mlse_equalizer = ML_MLSE("epochs_tr",150,"epochs_dd",1, ...
+% "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",80,"sps",1, ...
+% "traceback_depth",256,"L",1,"delta",4,"adaptive_mu",0);
+%
+% [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+% fprintf('ML-based MLSE:\n');
+% fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam2.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam2.m
new file mode 100644
index 0000000..e681f2e
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam2.m
@@ -0,0 +1,171 @@
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+Time_Rec = 1;
+if Time_Rec
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+ sps = 1;
+else
+ sps = 2;
+end
+
+if Time_Rec == 1 && M == 2
+ Rx_Time_Rec.fs = 14e9;
+elseif Time_Rec == 1 && M == 4
+ Rx_Time_Rec.fs = 6e9;
+end
+
+%% not working..
+Rx_synced = Rx_Time_Rec;
+% Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*4;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.004;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+sps = 1;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+%% -------------------- FFE --------------------
+% % requires some more digging what is going on :-)
+% eq_ffe = EQ("Ne",[200, 0, 0],"Nb",[0,0,0], ...
+% "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+% "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+% "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+% "eth_style_symbol_mapping",mapping_style);
+%
+% % ffe_results.metrics.print
+% fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+% BER_value = ffe_results.metrics.BER;
+
+%% -------------------- VNLE + MLSE --------------------
+
+pf_ncoeffs = 4;
+eq_v = EQ("Ne",[250, 0, 0],"Nb",[7, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",0, ...
+ 'weighted_DFE',1,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','I2','weighted_DFE_I_mode',[5,0.5,0.6]);
+pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+mlse_results.metrics.print
+fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- ML-based MLSE (L=2) --------------------
+% ml_mlse_equalizer = ML_MLSE("epochs_tr",400,"epochs_dd",1, ...
+% "len_tr",len_tr,"mu_dd",0.03,"mu_tr",0.03,"order",100,"sps",sps, ...
+% "traceback_depth",256,"L",2,"delta",4,"adaptive_mu",0);
+%
+% [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+% fprintf('ML-based MLSE:\n');
+% fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam4.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam4.m
new file mode 100644
index 0000000..2881ccf
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam4.m
@@ -0,0 +1,189 @@
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\Sweep Data\";
+mode = 0; %0 oder 1
+M = 4;
+
+baud_rate = '8';
+baud_rate_num = str2double(baud_rate);
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data_path = fullfile(base, baud_rate + "G_PAM2\tx_info\tx_info_PAM2_" + baud_rate + "Gbd0.75RRC.mat");
+ filename = fullfile(base, baud_rate + "G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, baud_rate + "G_PAM4\tx_info\tx_info_PAM4_" + baud_rate + "Gbd0.6RRC.mat");
+ filename = fullfile(base, baud_rate + "G_PAM4\M=4_Rs=" + baud_rate + "e9_Fs=8e10_I=255mA_RoP=8.4mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+% Rx_Time_Rec = Rx_matched;
+% Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+
+Time_Rec = 1;
+if Time_Rec
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+ sps = 1;
+else
+ sps = 2;
+end
+
+Rx_Time_Rec.fs = baud_rate_num * 10^9;
+
+% Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 12e9, 6e9);
+
+% Rx_matched_1.plot("fignum",231231)
+% Rx_Time_Rec.plot("fignum",231231)
+
+%% not working..
+Rx_synced = Rx_Time_Rec;
+% Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*2;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.004;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+%% -------------------- FFE --------------------
+% % requires some more digging what is going on :-)
+% eq_ffe = EQ("Ne",[400, 0, 0],"Nb",[0,0,0], ...
+% "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+% "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+% "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+% "eth_style_symbol_mapping",mapping_style);
+%
+% % ffe_results.metrics.print
+% fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+% BER_value = ffe_results.metrics.BER;
+
+%% -------------------- VNLE + MLSE --------------------
+
+pf_ncoeffs = 4;
+eq_v = EQ("Ne",[300, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+mlse_results.metrics.print
+fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- ML-based MLSE (L=2) --------------------
+% ml_mlse_equalizer = ML_MLSE("epochs_tr",200,"epochs_dd",1, ...
+% "len_tr",length(Rx_synced)/2,"mu_dd",0,"mu_tr",0.03,"order",100,"sps",sps, ...
+% "traceback_depth",256,"L",3,"delta",4,"adaptive_mu",0);
+%
+% [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+% fprintf('ML-based MLSE:\n');
+% fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- DB target --------------------
+% mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+%
+% eq_ = EQ("Ne",[50, 5, 5],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+% "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+%
+% dbt_results.metrics.print("description",'Duobinary');
+% fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_shift.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_shift.m
new file mode 100644
index 0000000..162d6a6
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_shift.m
@@ -0,0 +1,221 @@
+function BER = first_analysis_time_shift(time_shift)
+ %%
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+ mode = 0; %0 oder 1
+ M = 4;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ % data_tr_mf is the already recovered and filtered data
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+ Rx_matched_original = Rx_synced_cell{1};
+ Rx_matched_original.signal = resample(Rx_matched_original.signal,1,2);
+
+ [~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+ Time_Rec = 1;
+ if Time_Rec
+ % [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',1e-4,'detector_gain',2.7).process(Rx_matched_1);
+ Rx_Time_Rec = Time_Shifter('value',time_shift).process(Rx_matched_1);
+ Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal,1,2);
+ % figure(2222222)
+ % plot(Timing_Error)
+ sps = 1;
+ else
+ sps = 2;
+ end
+
+ if Time_Rec == 1 && M == 2
+ Rx_Time_Rec.fs = 14e9;
+ elseif Time_Rec == 1 && M == 4
+ Rx_Time_Rec.fs = 6e9;
+ end
+
+ Rx_Time_Rec.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+ % Rx_matched_original.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+ Rx_tr_mf.spectrum('normalizeTo0dB',1,"displayname",'Their signal','fignum',101111);
+
+ Rx_Time_Rec_plot = Rx_Time_Rec;
+ % Rx_matched_original_plot = Rx_matched_original;
+ Rx_tr_mf_plot = Rx_tr_mf;
+ Rx_Time_Rec_plot.normalize("mode","rms").plot("displayname",'Our signal','fignum',101311);
+ % Rx_matched_original_plot.normalize("mode","rms").plot("displayname",'Original signal','fignum',101311);
+ Rx_tr_mf_plot.normalize("mode","rms").plot("displayname",'Their signal','fignum',101311);
+
+ %% not working..
+ % Use our or their signal
+ for our_signal = 1
+ if our_signal
+ Rx_synced = Rx_Time_Rec;
+ else
+ Rx_synced = Rx_tr_mf;
+ end
+ % Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = 4096*2;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[500, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % % eq_ffe = FFE_DFE('ffe_order',99,'dfe_order',99,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5,'sps',sps,'decide',0);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % if our_signal
+ % fprintf('Our signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = 4;
+ eq_v = EQ("Ne",[100, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','R2','weighted_DFE_I_mode',[5,0.5,0.6]);
+ % eq_v = FFE_DFE('ffe_order',200,'dfe_order',0,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5, ...
+ % 'ffe_mu_dd',mu_ffe,'ffe_mu_tr',0,'dfe_mu_dd',mu_dfe,'dfe_mu_tr',0.005,'sps',sps,'decide',0);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ if our_signal
+ fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ else
+ fprintf('Their Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ end
+ BER = mlse_results.metrics.BER;
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",11,"sps",sps, ...
+ % "traceback_depth",256,"L",4,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % if our_signal
+ % fprintf('Our Signal: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+ end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m
new file mode 100644
index 0000000..fe8f371
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m
@@ -0,0 +1,271 @@
+%%
+clear all;
+close all;
+
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 4;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+% data_tr_mf is the already recovered and filtered data
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+%Calculate Transfer Function
+Tx_spectrum = Digi_sig_compare;
+Rx_spectrum = Scope_sig;
+
+[~,Rx_synced_spectrum,inverted_spectrum,sequenceFound_spectrum,sequenceStarts_spectrum] = Rx_spectrum.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_spectrum = Rx_synced_spectrum{1};
+
+Tx_spectrum = Tx_spectrum.resample("fs_out",fsym);
+Tx_spectrum.signal = fft(Tx_spectrum.signal);
+
+Rx_spectrum = Rx_spectrum.resample("fs_out",fsym);
+Rx_spectrum.signal = fft(Rx_spectrum.signal);
+
+H_transfer = Rx_spectrum.signal./Tx_spectrum.signal;
+H_inv = 1./H_transfer;
+
+%Number of Samples/Symbol after Matched Filter
+Kov = 14;
+
+Scope_sig = Scope_sig.resample('fs_in',fs,'fs_out',Kov*fsym);
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+% Rx_matched = Scope_sig;
+% Rx_matched = Rx_matched.resample('fs_out',Kov*fsym);
+
+data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+[~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+
+% Rx_matched = Scope_sig;
+% Rx_matched = Rx_matched.resample("fs_out",2*fsym);
+
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+Rx_matched_original = Rx_synced_cell{1};
+Rx_matched_original.signal = resample(Rx_matched_original.signal,1,Kov);
+Rx_matched_original.fs = fsym;
+
+% Rx_matched_1 = Rx_matched_1.resample("fs_in",Kov*fsym,'fs_out',2*fsym);
+
+Time_Rec = 1;
+if Time_Rec
+ % [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',Kov,'damping_factor',1,'normalized_loop_bandwidth',1e-4,'detector_gain',2.7).process(Rx_matched_1);
+
+ [Rx_Time_Rec, Timing_Error_MG] = Godard_Timing_Recovery('mode',3,'num_blocks',1,'fft_length',length(Rx_matched_1),'sps',Kov,'rolloff',0.6,'mu',-0.2,'Ki',1e-4).process(Rx_matched_1);
+ Rx_Time_Rec = Rx_Time_Rec.resample('fs_in',Kov*fsym,'fs_out',fsym);
+
+ % Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*128,'sps',Kov,'comp_signal',Rx_tr_mf,'comp_mode',0).process(Rx_matched_1);
+
+ sps = 1;
+else
+ Rx_Time_Rec = Rx_matched_1;
+ sps = 1;
+end
+
+Rx_Time_Rec.fs = fsym;
+
+% h = gaussdesign(0.4,64,1);
+% Rx_Time_Rec.signal = filtfilt(h,1,Rx_Time_Rec.signal);
+
+% Rx_com = Rx_Time_Rec;
+% Rx_com = Rx_com.resample('fs_in',fsym,'fs_out',2*fsym);
+% Rx_com.spectrum("displayname",'After TR','normalizeTo0dB',1,'fignum',101114);
+% Rx_matched_1.spectrum("displayname",'Before TR','normalizeTo0dB',1,'fignum',101114);
+% Rx_tr_mf_2sps = Rx_tr_mf;
+% Rx_tr_mf_2sps = Rx_tr_mf_2sps.resample('fs_in',fsym,'fs_out',2*fsym);
+% Rx_tr_mf_2sps.spectrum("displayname",'Their signal after TR','normalizeTo0dB',1,'fignum',101114);
+
+% Rx_Time_Rec = Rx_Time_Rec.resample('fs_out',6e9);
+% Rx_tr_mf = Rx_tr_mf.resample('fs_out',fsym);
+
+% Amax = 10;
+% H_inv = min(abs(H_inv), Amax) .* exp(1j*angle(H_inv));
+% Rx_Time_Rec.signal = fft(Rx_Time_Rec.signal);
+% Rx_Time_Rec.signal = ifft(Rx_Time_Rec.signal .* H_inv);
+
+Rx_Time_Rec = Rx_Time_Rec.normalize('mode','rms');
+Rx_tr_mf = Rx_tr_mf.normalize('mode','rms');
+
+Rx_Time_Rec.spectrum("displayname",'Our signal','normalizeTo0dB',1,'fignum',101111);
+% Rx_matched_original.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+Rx_tr_mf.spectrum("displayname",'Their signal','normalizeTo0dB',1,'fignum',101111);
+
+Rx_Time_Rec.normalize("mode","rms").plot("displayname",'Our signal','fignum',101311);
+% Rx_matched_original.normalize("mode","rms").plot("displayname",'Original signal','fignum',101311);
+Rx_tr_mf.normalize("mode","rms").plot("displayname",'Their signal','fignum',101311);
+
+%% not working..
+% Use our or their signal
+for our_signal = 1
+ if our_signal
+ Rx_synced = Rx_Time_Rec;
+ else
+ Rx_synced = Rx_tr_mf;
+ end
+ % Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = 4096*2;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[150, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % % eq_ffe = FFE_DFE('ffe_order',99,'dfe_order',99,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5,'sps',sps,'decide',0);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % if our_signal
+ % fprintf('Our signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = 4;
+ eq_v = EQ("Ne",[300, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','R2','weighted_DFE_I_mode',[5,0.5,0.6]);
+ % eq_v = FFE_DFE('ffe_order',300,'dfe_order',5,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5, ...
+ % 'ffe_mu_dd',mu_ffe,'ffe_mu_tr',0,'dfe_mu_dd',mu_dfe,'dfe_mu_tr',0.005,'sps',sps,'decide',0);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ if our_signal
+ fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ else
+ fprintf('Their Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ end
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",11,"sps",sps, ...
+ % "traceback_depth",256,"L",4,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % if our_signal
+ % fprintf('Our Signal: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam2.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam2.m
new file mode 100644
index 0000000..e6f4c45
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam2.m
@@ -0,0 +1,195 @@
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ % Already recovered and filtered data
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+[~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+
+Time_Rec = 1;
+if Time_Rec
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+ sps = 1;
+else
+ sps = 2;
+end
+
+if Time_Rec == 1 && M == 2
+ Rx_Time_Rec.fs = 14e9;
+elseif Time_Rec == 1 && M == 4
+ Rx_Time_Rec.fs = 6e9;
+end
+
+Rx_Time_Rec.spectrum("displayname",'Signal after matched filter, synchronization, and timing recovery','fignum',101111);
+Rx_tr_mf.spectrum("displayname",'Signal after matched filter, synchronization, and timing recovery','fignum',101111);
+
+Rx_Time_Rec_plot = Rx_Time_Rec;
+Rx_tr_mf_plot = Rx_tr_mf;
+Rx_Time_Rec_plot.normalize("mode","rms").plot("displayname",'Signal after matched filter, synchronization, and timing recovery','fignum',101311);
+Rx_tr_mf_plot.normalize("mode","rms").plot("displayname",'Signal after matched filter, synchronization, and timing recovery','fignum',101311);
+
+%% not working..
+% Use our or their signal
+our_signal = 0;
+if our_signal
+ Rx_synced = Rx_Time_Rec;
+else
+ Rx_synced = Rx_tr_mf;
+end
+% Rx_synced = Rx_Time_Rec;
+% Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*2;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.004;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+sps = 1;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+%% -------------------- FFE --------------------
+% % requires some more digging what is going on :-)
+% eq_ffe = EQ("Ne",[200, 0, 0],"Nb",[0,0,0], ...
+% "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+% "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+% "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+% "eth_style_symbol_mapping",mapping_style);
+%
+% % ffe_results.metrics.print
+% fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+% BER_value = ffe_results.metrics.BER;
+
+%% -------------------- VNLE + MLSE --------------------
+
+pf_ncoeffs = 4;
+eq_v = EQ("Ne",[200, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','I2','weighted_DFE_I_mode',[5,0.5,0.6]);
+pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+mlse_results.metrics.print
+fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- ML-based MLSE (L=2) --------------------
+% ml_mlse_equalizer = ML_MLSE("epochs_tr",400,"epochs_dd",1, ...
+% "len_tr",len_tr,"mu_dd",0.03,"mu_tr",0.03,"order",100,"sps",sps, ...
+% "traceback_depth",256,"L",2,"delta",4,"adaptive_mu",0);
+%
+% [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+% fprintf('ML-based MLSE:\n');
+% fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam_2.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam_2.m
new file mode 100644
index 0000000..d27d6e1
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam_2.m
@@ -0,0 +1,272 @@
+%%
+clear all;
+close all;
+
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+% data_tr_mf is the already recovered and filtered data
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+%Calculate Transfer Function
+Tx_spectrum = Digi_sig_compare;
+Rx_spectrum = Scope_sig;
+
+[~,Rx_synced_spectrum,inverted_spectrum,sequenceFound_spectrum,sequenceStarts_spectrum] = Rx_spectrum.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_spectrum = Rx_synced_spectrum{1};
+
+Tx_spectrum = Tx_spectrum.resample("fs_out",fsym);
+Tx_spectrum.signal = fft(Tx_spectrum.signal);
+
+Rx_spectrum = Rx_spectrum.resample("fs_out",fsym);
+Rx_spectrum.signal = fft(Rx_spectrum.signal);
+
+H_transfer = Rx_spectrum.signal./Tx_spectrum.signal;
+H_inv = 1./H_transfer;
+
+%Number of Samples/Symbol after Matched Filter
+Kov = 14;
+
+Scope_sig = Scope_sig.resample('fs_in',fs,'fs_out',Kov*fsym);
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+% Rx_matched = Scope_sig;
+% Rx_matched = Rx_matched.resample('fs_out',Kov*fsym);
+
+data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+[~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+
+% Rx_matched = Scope_sig;
+% Rx_matched = Rx_matched.resample("fs_out",2*fsym);
+
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+Rx_matched_original = Rx_synced_cell{1};
+Rx_matched_original.signal = resample(Rx_matched_original.signal,1,Kov);
+Rx_matched_original.fs = fsym;
+
+% Rx_matched_1 = Rx_matched_1.resample("fs_in",Kov*fsym,'fs_out',2*fsym);
+
+Time_Rec = 1;
+if Time_Rec
+ % [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',Kov,'damping_factor',1,'normalized_loop_bandwidth',1e-4,'detector_gain',2.7).process(Rx_matched_1);
+ % Rx_matched_1.spectrum('normalizeTo0dB',1,"displayname",'Signal before Timing Recovery','fignum',1023);
+ % [Rx_Time_Rec, Timing_Error_MG] = Godard_Timing_Recovery('mode',3,'num_blocks',1,'fft_length',length(Rx_matched_1),'sps',Kov,'rolloff',0.6,'mu',-0.2,'Ki',1e-4).process(Rx_matched_1);
+ Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*16,'sps',Kov,'comp_signal',Rx_tr_mf,'comp_mode',0).process(Rx_matched_1);
+ % Rx_Time_Rec.spectrum('normalizeTo0dB',1,"displayname",'Signal after Timing Recovery','fignum',1023);
+ % idx = 1:2:length(Rx_Time_Rec);
+ % Rx_Time_Rec.signal = Rx_Time_Rec.signal(idx);
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal,1,Kov);
+ % Rx_Time_Rec = Rx_matched_1;
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal,1,Kov);
+ % figure(2222222)
+ % plot(Timing_Error)
+ sps = 1;
+else
+ Rx_Time_Rec = Rx_matched_1;
+ sps = 1;
+end
+Rx_Time_Rec.fs = fsym;
+
+Rx_com = Rx_Time_Rec;
+Rx_com = Rx_com.resample('fs_in',fsym,'fs_out',2*fsym);
+Rx_com.spectrum("displayname",'After TR','normalizeTo0dB',1,'fignum',101114);
+Rx_matched_1.spectrum("displayname",'Before TR','normalizeTo0dB',1,'fignum',101114);
+Rx_tr_mf_2sps = Rx_tr_mf;
+Rx_tr_mf_2sps = Rx_tr_mf_2sps.resample('fs_in',fsym,'fs_out',2*fsym);
+Rx_tr_mf_2sps.spectrum("displayname",'Their signal after TR','normalizeTo0dB',1,'fignum',101114);
+
+% Rx_Time_Rec = Rx_Time_Rec.resample('fs_out',6e9);
+% Rx_tr_mf = Rx_tr_mf.resample('fs_out',fsym);
+
+% Amax = 10;
+% H_inv = min(abs(H_inv), Amax) .* exp(1j*angle(H_inv));
+% Rx_Time_Rec.signal = fft(Rx_Time_Rec.signal);
+% Rx_Time_Rec.signal = ifft(Rx_Time_Rec.signal .* H_inv);
+
+Rx_Time_Rec = Rx_Time_Rec.normalize('mode','rms');
+Rx_tr_mf = Rx_tr_mf.normalize('mode','rms');
+
+Rx_Time_Rec.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+% Rx_matched_original.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+Rx_tr_mf.spectrum('normalizeTo0dB',1,"displayname",'Their signal','fignum',101111);
+
+Rx_Time_Rec.normalize("mode","rms").plot("displayname",'Our signal','fignum',101311);
+% Rx_matched_original.normalize("mode","rms").plot("displayname",'Original signal','fignum',101311);
+Rx_tr_mf.normalize("mode","rms").plot("displayname",'Their signal','fignum',101311);
+
+%% not working..
+% Use our or their signal
+for our_signal = 1
+ if our_signal
+ Rx_synced = Rx_Time_Rec;
+ else
+ Rx_synced = Rx_tr_mf;
+ end
+ % Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = 4096*2;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[500, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % % eq_ffe = FFE_DFE('ffe_order',99,'dfe_order',99,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5,'sps',sps,'decide',0);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % if our_signal
+ % fprintf('Our signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = 4;
+ eq_v = EQ("Ne",[250, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','R2','weighted_DFE_I_mode',[5,0.5,0.6]);
+ % eq_v = FFE_DFE('ffe_order',200,'dfe_order',0,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5, ...
+ % 'ffe_mu_dd',mu_ffe,'ffe_mu_tr',0,'dfe_mu_dd',mu_dfe,'dfe_mu_tr',0.005,'sps',sps,'decide',0);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ if our_signal
+ fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ else
+ fprintf('Their Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ end
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",11,"sps",sps, ...
+ % "traceback_depth",256,"L",4,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % if our_signal
+ % fprintf('Our Signal: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/ml_mlse_complexity_plot.m b/projects/FSO_transmission/Evaluation Scripts/ml_mlse_complexity_plot.m
new file mode 100644
index 0000000..d30035b
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/ml_mlse_complexity_plot.m
@@ -0,0 +1,11 @@
+N = 1:4;
+S = 4;
+L = 1:4;
+Complexity_N = (N+1).*S.^(1+1);
+Complexity_L = (1+1).*S.^(L+1);
+
+figure;
+plot(N,Complexity_N)
+hold on
+plot(L,Complexity_L)
+hold off
diff --git a/projects/FSO_transmission/Evaluation Scripts/plotscript_baud_rate_sweep_method.m b/projects/FSO_transmission/Evaluation Scripts/plotscript_baud_rate_sweep_method.m
new file mode 100644
index 0000000..ae3a1b5
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/plotscript_baud_rate_sweep_method.m
@@ -0,0 +1,36 @@
+x = 4:8;
+for method = 1:4
+ filename = fullfile('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\Data', ['BER_PAM_4_baud_rate_method' num2str(method) '.mat']);
+ data = load(filename);
+ BER = data.BER_PAM_4;
+ figure(22113344)
+ plot(x,BER,'-o','LineWidth',1.75);
+ hold on
+end
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% Legende NUR für Kurven
+legend('FFE - 300 Taps','FFE+PF+MLSE - 300 FFE Taps - 4 PF Coefficients','ML-MLSE - 1000 epochs - 100th order - Memory Length = 2','DB - 300 FFE Taps',...
+ 'Interpreter','latex', ...
+ 'Location','southeast', 'FontSize', 14)
+
+% FEC Labels direkt im Plot
+text(6.85,2.3e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(7,3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(7,5.6e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(7,2.5e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Symbol Rate [GBd]', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-4', 'Interpreter','latex')
+
+grid minor
+% ylim([1e-4 5e-2])
+xlim([3 9])
+set(gca,'YScale','log')
+hold off
+% beautifyBERplot
\ No newline at end of file
diff --git a/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m b/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m
new file mode 100644
index 0000000..36884ff
--- /dev/null
+++ b/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m
@@ -0,0 +1,251 @@
+%%
+clear all;
+close all;
+
+%% Choose a fitting base leading to the FSO Data
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 4;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+% data_tr_mf is the already recovered and filtered data
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+%Calculate Transfer Function
+Tx_spectrum = Digi_sig_compare;
+Rx_spectrum = Scope_sig;
+
+[~,Rx_synced_spectrum,inverted_spectrum,sequenceFound_spectrum,sequenceStarts_spectrum] = Rx_spectrum.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_spectrum = Rx_synced_spectrum{1};
+
+Tx_spectrum = Tx_spectrum.resample("fs_out",fsym);
+Tx_spectrum.signal = fft(Tx_spectrum.signal);
+
+Rx_spectrum = Rx_spectrum.resample("fs_out",fsym);
+Rx_spectrum.signal = fft(Rx_spectrum.signal);
+
+H_transfer = Rx_spectrum.signal./Tx_spectrum.signal;
+H_inv = 1./H_transfer;
+
+%Number of Samples/Symbol after Matched Filter
+Kov = 14;
+
+Scope_sig = Scope_sig.resample('fs_in',fs,'fs_out',Kov*fsym);
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+% Loading and synchronizing their matched filtered and timing recovered data
+data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+[~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+% Timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+% Timing recovery
+Time_Rec = 1;
+Timing_Mode = 3;
+if Time_Rec
+ if Timing_Mode == 1 % Zero-Crossing/Gardner/Early-Late/Müller-Mueller Timing Recovery
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',Kov,'damping_factor',1,'normalized_loop_bandwidth',1e-4,'detector_gain',2.7).process(Rx_matched_1);
+ elseif Timing_Mode == 2 % Godard Timing Recovery (resampling is required as the Godard timing recovery does not change the number of samples per symbol)
+ [Rx_Time_Rec, Timing_Error_MG] = Godard_Timing_Recovery('mode',3,'num_blocks',1,'fft_length',length(Rx_matched_1),'sps',Kov,'rolloff',0.6,'mu',-0.2,'Ki',1e-4).process(Rx_matched_1);
+ Rx_Time_Rec = Rx_Time_Rec.resample('fs_in',Kov*fsym,'fs_out',fsym);
+ elseif Timing_Mode == 3 % Maximum Variance Timing Recovery
+ Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*128,'sps',Kov,'comp_signal',Rx_tr_mf,'comp_mode',0).process(Rx_matched_1);
+ end
+else
+ Rx_Time_Rec = Rx_matched_1.resample('fs_in',Kov*fsym,'fs_out',fsym);
+end
+sps = 1;
+Rx_Time_Rec.fs = fsym;
+
+% % Compensate using the inverse transfer function
+% Amax = 10;
+% H_inv = min(abs(H_inv), Amax) .* exp(1j*angle(H_inv));
+% Rx_Time_Rec.signal = fft(Rx_Time_Rec.signal);
+% Rx_Time_Rec.signal = ifft(Rx_Time_Rec.signal .* H_inv);
+
+% Normalization
+Rx_Time_Rec = Rx_Time_Rec.normalize('mode','rms');
+Rx_tr_mf = Rx_tr_mf.normalize('mode','rms');
+
+% Compare spectra of our and their time signal
+Rx_Time_Rec.spectrum("displayname",'Our signal','normalizeTo0dB',1,'fignum',101111);
+% Rx_matched_original.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+Rx_tr_mf.spectrum("displayname",'Their signal','normalizeTo0dB',1,'fignum',101111);
+
+% Compare our and their time signal
+Rx_Time_Rec.normalize("mode","rms").plot("displayname",'Our signal','fignum',101311);
+% Rx_matched_original.normalize("mode","rms").plot("displayname",'Original signal','fignum',101311);
+Rx_tr_mf.normalize("mode","rms").plot("displayname",'Their signal','fignum',101311);
+
+%%
+% Use our or their signal
+for our_signal = 1
+ if our_signal
+ Rx_synced = Rx_Time_Rec;
+ else
+ Rx_synced = Rx_tr_mf;
+ end
+ % Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = 4096*2;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[150, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % % eq_ffe = FFE_DFE('ffe_order',99,'dfe_order',99,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5,'sps',sps,'decide',0);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % if our_signal
+ % fprintf('Our signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = 4;
+ eq_v = EQ("Ne",[300, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','R2','weighted_DFE_I_mode',[5,0.5,0.6]);
+ % eq_v = FFE_DFE('ffe_order',300,'dfe_order',5,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5, ...
+ % 'ffe_mu_dd',mu_ffe,'ffe_mu_tr',0,'dfe_mu_dd',mu_dfe,'dfe_mu_tr',0.005,'sps',sps,'decide',0);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ if our_signal
+ fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ else
+ fprintf('Their Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ end
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",11,"sps",sps, ...
+ % "traceback_depth",256,"L",4,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % if our_signal
+ % fprintf('Our Signal: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/first_analysis.m b/projects/FSO_transmission/first_analysis.m
new file mode 100644
index 0000000..9a327ab
--- /dev/null
+++ b/projects/FSO_transmission/first_analysis.m
@@ -0,0 +1,252 @@
+
+base = "C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC";
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_tx_compare = Pform.process(Symbols);
+
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sync = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_tx_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+timesig_compare = [0:1:datas.tr.lastData(1).trace.ch3.Points-1] ./ fs;
+timesig = datas.tr.lastData(1).trace.ch3.XData;
+assert(isequal(traceData.YData,scoperead_volts));
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+%%
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef directly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+apply_matched_filter = 1;
+k = 1;
+if apply_matched_filter
+
+ Pform = Pulseformer("fsym",fsym,"fdac",k*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Pform.process(Scope_sig);
+else
+
+ Rx_matched = Filter('filtdegree',4,"f_cutoff",fsym*0.5,"fs",Scope_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scope_sig);
+ Rx_matched = Rx_matched.resample("fs_out",k*fsym);
+end
+Rx_matched.spectrum();
+
+
+%%
+
+coefficients = arburg(Rx_matched.signal,25);
+
+figure()
+[h,w] = freqz(1,coefficients,Rx_matched.length,"whole",Rx_matched.fs);
+h = h/max(abs(h));
+hold on
+w_ = (w - Rx_matched.fs/2);
+plot(w_.*1e-9,20*log10(fftshift(abs(h))),'DisplayName',['Burg Coeffs: ', num2str(round(coefficients,2)), ' '],'LineWidth',2);
+
+%% Timing Rec
+apply_timing_rec = 1;
+if apply_timing_rec
+ [Rx_symbolsync, timing_error] = Timing_Recovery("timing_error_detector",'Gardner (non-data-aided)','sps',k,'damping_factor',0.1,'normalized_loop_bandwidth',0.1,'detector_gain',2.7).process(Rx_matched);
+ figure();plot(timing_error);
+ Rx_symbolsync.fs = fsym;
+ % Tsynch
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
+ Rx_synced = Rx_synced_cell{1};
+ sps = 1;
+else
+ % Tsynch
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
+ Rx_synced = Rx_synced_cell{1};
+ Rx_synced = Rx_synced.resample("fs_out",2*fsym);
+ sps = 2;
+end
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+
+%%
+
+Rx_synced = Rx_synced_cell{1};
+
+
+% -------------------- FFE --------------------
+% requires some more digging what is going on :-)
+eq_ffe = EQ("Ne",[50, 1, 1],"Nb",[2,0,0], ...
+ "training_length",512,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+vars = logspace(-4,-3,36);
+
+parfor i = 1:numel(vars)
+
+ len_tr = 4096;
+ mu_ffe1 = 0.01;% mus(i);%0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.005;
+ mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
+ mu_dfe = vars(i);
+ duob_mode = db_mode.no_db;
+
+ % requires some more digging what is going on :-)
+ eq_ffe_1 = EQ("Ne",[150, 1, 0],"Nb",[50,0,0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",0);
+
+ eq_ffe_2 = FFE("epochs_tr",1,"epochs_dd",vars(i),"len_tr",4096,"mu_dd",vars(i),"mu_tr",vars(i),"order",999,"sps",1,"decide",0, "adaption",adaption_method.nlms,"dd_mode",0);
+ % eq_ffe_2 = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-5,"dfe_mu_dd",mus(i),"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",50,"dfe_order",10,"sps",1,"decide",1);
+
+
+ ffe_results = ffe(eq_ffe_1,M,Rx_synced,Symbols,Bits, ...
+ "precode_mode",duob_mode,'showAnalysis',1,"postFFE",[], ...
+ "eth_style_symbol_mapping",mapping_style);
+
+ ffe_results.metrics.BER
+ bers(i) = ffe_results.metrics.BER;
+end
+
+figure();
+plot(vars,bers);
+yline(ber_in_paper)
+beautifyBERplot();
+%
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+ffe_results.metrics.print("description",'FFE');
+fprintf('FFE: %.1e \n',ffe_results.metrics.BER);
+
+
+%% -------------------- VNLE + MLSE --------------------
+len_tr = 4096;
+mu_ffe1 = 0.0001;% mus(i);%0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.005;
+mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+
+pf_ncoeffs = 4;
+
+vars = 1:7;
+bers = zeros(size(vars));
+parfor i = 1:numel(vars)
+
+ eqv = EQ("Ne",[200, 1, 0],"Nb",[2, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ncoeffs = vars(i);
+ pf = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eqv, pf, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ vnle_results.metrics.print("description",'VNLE');
+ mlse_results.metrics.print("description",'MLSE');
+ bers(i) = mlse_results.metrics.BER;
+end
+%%
+figure();hold on
+plot(vars,bers_ffe,'DisplayName','FFE [200,0,0] + PF + MLSE');
+plot(vars,bers_vnle,'DisplayName','VNLE [200,1,0] + PF + MLSE');
+plot(vars,bers_vnledfe,'DisplayName','VNLE [200,1,0] + DFE [2] + PF + MLSE');
+plot(vars,bers_vnledfe_ideal,'DisplayName','VNLE [200,1,0] + ideal DFE [2] + PF + MLSE');
+yline(ber_in_paper);
+yline([2e-2, 4.85e-3, 3.8e-3, 2,2e-4],'LineWidth',2,'Color',[0.8,0.8,0.8],'LineStyle',':','HandleVisibility','off');
+ylim([1e-5,0.1]);
+beautifyBERplot();
+
+%% -------------------- DB target --------------------
+mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+
+eq_ = EQ("Ne",[50, 5, 5],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+dbt_results.metrics.print("description",'Duobinary');
+
+
+%%
+
+%ML-based MLSE (L=2)
+mu_ml = 0.01; training_epochs = 100;
+ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
+ "len_tr",len_tr,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",1, ...
+ "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0);
+
+[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode);
+ml_mlse_results.metrics.print("description",'ML ');
\ No newline at end of file
diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m
index 21f881f..986fcdb 100644
--- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m
+++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m
@@ -6,7 +6,7 @@ db = DBHandler("dataBase", "labor_highspeed", "type", database_type);
pam_levels = [4, 6, 8]; % three tiles
bitrate_set = 360e9;
-fiberL = 10;
+fiberL = 2;
fields = [
db.getTableFieldNames('power_state_info');
@@ -96,7 +96,7 @@ end
%% ============================================================
% PLOT — 1×3 (PAM-4, PAM-6, PAM-8)
% ============================================================
-fig = figure(9110); clf;
+fig = figure(9112); clf;
tiledlayout(1,3,'TileSpacing','compact','Padding','compact');
lw = 1.8;
@@ -223,19 +223,19 @@ ylabel('');
end
-pos = 1e3.*[2.7770 1.2017 1.4000 0.3200];
-set(fig, 'Position', pos);
+% pos = 1e3.*[2.7770 1.2017 1.4000 0.3200];
+% set(fig, 'Position', pos);
%% === EXPORT ===
-outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\wavelength_analysis.tikz';
-matlab2tikz(outfile, ...
- 'width','\fwidth', ...
- 'height','\fheight', ...
- 'showInfo',false, ...
- 'extraAxisOptions',{ ...
- 'legend style={font=\footnotesize}', ...
- 'legend columns=1' ...
- });
+% outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\wavelength_analysis.tikz';
+% matlab2tikz(outfile, ...
+% 'width','\fwidth', ...
+% 'height','\fheight', ...
+% 'showInfo',false, ...
+% 'extraAxisOptions',{ ...
+% 'legend style={font=\footnotesize}', ...
+% 'legend columns=1' ...
+% });
diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m
index 2213c8a..147c7d9 100644
--- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m
+++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m
@@ -1,83 +1,139 @@
+tablename = 'C:\Users\Silas\Documents\latex\JLT_400G_submission\HighSpeedExperiments_oneandonly_csv.csv';
+% Returns a Table
+data = readtable(tablename,"Delimiter",';','DecimalSeparator',',');
+
+
%% ============================================================
% PLOT
% ============================================================
+%% 1. DATA EXTRACTION & SETUP
+%% 1. DATA EXTRACTION & SETUP
+raw_M = data.M;
+raw_baud = data.BaudRate;
+raw_net = data.NetRate;
+raw_codes = string(data.ZoteroCode);
+raw_names = string(data.Name);
+raw_band = string(data.Band);
+
+% Filter Valid Data
+target_M = [2, 4, 6, 8];
+validIdx = ismember(raw_M, target_M) & ~isnan(raw_baud) & ~isnan(raw_net);
+
+Mvals = raw_M(validIdx);
+baud = raw_baud(validIdx);
+netrate = raw_net(validIdx);
+codes = raw_codes(validIdx);
+names = raw_names(validIdx);
+bands = raw_band(validIdx);
+
+pam_list = target_M;
+colors = flip(cbrewer2('SET1',4));
+
+%% 2. PLOT (For Visual Check only)
figure; hold on;
-ms = 32; % scatter size
-lw = 0.8; % line width
-
-for k = 1:4 % PAM-2/4/6/8
+ms = 20;
+lw = 0.5;
+for k = 1:length(pam_list)
M = pam_list(k);
idxPam = (Mvals == M);
-
- % Extract for this PAM
+
x = baud(idxPam);
y = netrate(idxPam);
+ b = bands(idxPam);
n = names(idxPam);
-
- % Get color for this PAM format
col = colors(k,:);
-
- % ----- LEGEND FLAG (only add one entry per PAM) -----
+
firstLegend = true;
-
- % ---- PLOT ALL POINTS (marker based on publication) ----
+
for i = 1:sum(idxPam)
- % marker selection by publication
- pubIdx = find(pub_list == n(i), 1);
- marker = markerlist{mod(pubIdx-1, nMarkers) + 1};
-
+ % Marker Logic
+ ms = 20;
+ if strcmpi(b(i), 'O')
+ marker = 'o';
+ elseif strcmpi(b(i), 'C')
+ marker = 'd';
+ else
+ marker = 's';
+ end
+ if strcmpi(n(i), 'THIS WORK')
+ marker = 'pentagram';
+ ms = 100;
+ end
+
+ % Plot Scatter
if firstLegend
- h = scatter(x(i), y(i), ms, ...
- 'Marker', marker, ...
- 'MarkerEdgeColor', col, ...
- 'MarkerFaceColor', col, ...
+ scatter(x(i), y(i), ms, 'Marker', marker, ...
+ 'MarkerEdgeColor', col, 'MarkerFaceColor', col, ...
'DisplayName', sprintf('PAM-%d', M));
firstLegend = false;
else
- h = scatter(x(i), y(i), ms, ...
- 'Marker', marker, ...
- 'MarkerEdgeColor', col, ...
- 'MarkerFaceColor', col, ...
+ scatter(x(i), y(i), ms, 'Marker', marker, ...
+ 'MarkerEdgeColor', col, 'MarkerFaceColor', col, ...
'HandleVisibility','off');
end
-
- % ====== CUSTOM DATATIP CONTENT ======
- dt = h.DataTipTemplate;
- dt.DataTipRows(1).Label = 'Baud rate';
- dt.DataTipRows(2).Label = 'Net rate';
-
- % Add publication name
- dt.DataTipRows(end+1) = dataTipTextRow('Publication', n(i));
-
-
end
-
- % ---- Fit (PAM-specific) ----
- valid = ~isnan(x) & ~isnan(y);
- if sum(valid) >= 3
- p = polyfit(x(valid), y(valid), 2);
- xfit = linspace(min(x(valid)), max(x(valid)), 200);
- yfit = polyval(p, xfit);
-
- plot(xfit, yfit, ':', ...
- 'LineWidth', lw, ...
- 'Color', col, ...
- 'HandleVisibility', 'off'); % do NOT add to legend
+
+ % Fit lines
+ if length(x) >= 3
+ [p, S, mu] = polyfit(x, y, 2);
+ xfit = linspace(min(x), max(x), 200);
+ yfit = polyval(p, xfit, S, mu);
+ plot(xfit, yfit, '-', 'LineWidth', lw, 'Color', col, 'HandleVisibility', 'off');
end
end
-grid on;
+grid on; box on;
xlabel('Baud rate [GBd]');
ylabel('Net rate [Gb/s]');
-
-legend('Location','northwest');
-set(gca,'FontSize',11);
-
+% title('Check Command Window for TikZ Code');
+% legend('Location','northwest');
+
+%% 3. GENERATE TIKZ ANNOTATION CODE
+% This prints the manual \draw commands to the console
+
+%% GENERATE TIKZ ANNOTATION CODE
+% This prints the manual \draw commands to the console
+
+%% GENERATE TIKZ ANNOTATION CODE (Colored Borders + Tiny Font)
+%% GENERATE TIKZ ANNOTATION CODE (No Arrow, Close Text)
+fprintf('\n\n%% ===========================================================\n');
+fprintf('%% COPY THE FOLLOWING LINES INTO YOUR .TEX FILE \n');
+fprintf('%% (Paste them just before \\end{axis})\n');
+fprintf('%% ===========================================================\n\n');
+
+for i = 1:length(baud)
+ bx = baud(i);
+ by = netrate(i);
+ key = codes(i);
+ M_val = Mvals(i);
+
+ % --- PLACEMENT LOGIC ---
+ if M_val == 8
+ % PAM-8: Place Top-Left
+ % 'south east' anchor means the text's bottom-right corner touches the coordinate
+ % shift moves it slightly up and left to clear the marker
+ anchorStr = 'south east';
+ shiftStr = 'shift={(-3pt, 3pt)}';
+ else
+ % Others: Place Bottom-Right
+ % 'north west' anchor means the text's top-left corner touches the coordinate
+ % shift moves it slightly down and right
+ anchorStr = 'north west';
+ shiftStr = 'shift={(3pt, -3pt)}';
+ end
+
+ % --- PRINT COMMAND ---
+ % Uses \node directly at the coordinate (axis cs:...)
+ fprintf('\\node[anchor=%s, %s, font=\\tiny, fill=white, inner sep=1pt] at (axis cs:%.2f, %.2f) {\\cite{%s}};\n', ...
+ anchorStr, shiftStr, bx, by, key);
+end
+fprintf('\n')
%% === EXPORT ===
-outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\highspeedresults.tikz';
+outfile = 'C:\Users\Silas\Documents\latex\JLT_400G_submission\media\matlab2tikz\highspeedresults_test.tikz';
+
matlab2tikz(outfile, ...
'width','\fwidth', ...
'height','\fheight', ...
diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m
index ab31451..67a2ff8 100644
--- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m
+++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m
@@ -10,10 +10,11 @@ if dsp_options.mode == "load_run_id"
if experiment == "highspeed_2024"
- dsp_options.database_type = 'mysql';
+ dsp_options.database_type = "mysql";
dsp_options.dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
- db = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type);
+ db = DBHandler("dataBase", [dsp_options.dataBase],...
+ "type", dsp_options.database_type,"server","192.168.178.192","user","silas","password","silas");
elseif experiment == "mpi_ecoc_2025"
diff --git a/projects/ML_based_MLSE/analyze_filter_length.m b/projects/ML_based_MLSE/analyze_filter_length.m
index 616b715..1b31d6b 100644
--- a/projects/ML_based_MLSE/analyze_filter_length.m
+++ b/projects/ML_based_MLSE/analyze_filter_length.m
@@ -5,9 +5,9 @@ M = 4;
randkey = 1;
% --- Parameter sweep
-order_range = 2:3:11; % FFE order
-delta_range = 0:2:4; % delta
-SNR_dB = 20;
+order_range = 5:5:50; % FFE order
+delta_range = 0:5:20; % delta
+SNR_dB = 30;
% --- Prepare bit sequence
order_bits = 19;
@@ -21,10 +21,14 @@ Symbols = PAMmapper(M,0).map(Bits);
Symbols.fs = 200e9;
% --- Channel (minimal ISI + AWGN)
-h = [0.3 0.9 0.3]; h = h/norm(h);
+h = abs([0.3 0.9 0.3]); h = h/norm(h);
+
+% h = [1 -1.67085330039878 1.17918163282514 -0.805210559745616 0.571564213123367 -0.296337147529674 0.00649773445209780 0.0854177610195952 -0.0576009020965258 0.0520994427061551 -0.0624586034913656 0.0553280962699552 -0.00705582559925755 -0.0336399056707792 0.0706903719452810 -0.0334124287931977 0.0131699455037966 0.0587431373842994 -0.0515902976066452 0.00647904355473619 0.0137506750904990 -0.0547974515885928 0.00994735499340592 -0.0135513582534086 -0.00463322575007739 0.0277311946101940];
+% h = h/norm(h);
symbols_filt = Symbols.filter(h,1);
symbols_noi = symbols_filt;
symbols_noi.signal = awgn(symbols_filt.signal,SNR_dB,'measured');
+symbols_noi.spectrum();
% --- Generate all parameter pairs
[O,D] = ndgrid(order_range, delta_range);
@@ -38,7 +42,7 @@ ce_vec = nan(size(pairs,1),1);
ce_training = nan(size(pairs,1),training_len);
% --- Parallel loop over parameter pairs
-parfor k = 1:size(pairs,1)
+for k = 1:size(pairs,1)
order_k = pairs(k,1);
delta_k = pairs(k,2);
@@ -89,7 +93,7 @@ end
beautifyBERplot
ylabel('BER'); xlabel('Filter Order [N]');
title('BER vs. Filter order');
-ylim([1e-4, 0.1]);
+% ylim([1e-4, 0.1]);
yline(3.8e-3,'HandleVisibility','off');
yline(2.2e-4,'HandleVisibility','off');
diff --git a/projects/ML_based_MLSE/theoretic_channel_evaluation.m b/projects/ML_based_MLSE/theoretic_channel_evaluation.m
index 3e1efcc..3eb6f09 100644
--- a/projects/ML_based_MLSE/theoretic_channel_evaluation.m
+++ b/projects/ML_based_MLSE/theoretic_channel_evaluation.m
@@ -48,36 +48,36 @@ for i = 1:numel(SNR_dB)
symbols_noi = symbols_filt;
symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB(i), 'measured'); % AWGN with given SNR
- % % Sequence Est L=5
- % mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',h);
- % mlse_.DIR = h;
- % [y_mlse] = mlse_.process(symbols_noi,Symbols);
- % mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
- % [~, ~, ber_mlse_l5(i), ~] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
- % fprintf('MLSE L5: %.2e \n',ber_mlse_l5(i));
- %
- % % 2nd Approach
- % mu_lms = 0.0005;
- % pf_ncoeffs = 1;
- % eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",16,"sps",1,"dd_mode",1,"adaption_technique","lms");
- % pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
- %
- % % FFE
- % [y_ffe, ffe_noise] = eq_.process(symbols_noi, Symbols);
- %
- % Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ffe);
- % [~, ~, ber_ffe(i), ~] = calc_ber(Eq_bits.signal, Bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
- % fprintf('FFE: %.2e \n',ber_ffe(i));
- %
- % % Postfilter
- % [y_white,~] = pf_.process(y_ffe, ffe_noise);
- %
- % % Sequence Est
- % mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
- % [y_mlse] = mlse_.process(y_white,Symbols);
- % mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
- % [~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
- % fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i));
+ % Sequence Est L=5
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',h);
+ mlse_.DIR = h;
+ [y_mlse] = mlse_.process(symbols_noi,Symbols);
+ mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
+ [~, ~, ber_mlse_l5(i), ~] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
+ fprintf('MLSE L5: %.2e \n',ber_mlse_l5(i));
+
+ % 2nd Approach
+ mu_lms = 0.0005;
+ pf_ncoeffs = 1;
+ eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",16,"sps",1,"dd_mode",1,"adaption_technique","lms");
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+
+ % FFE
+ [y_ffe, ffe_noise] = eq_.process(symbols_noi, Symbols);
+
+ Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ffe);
+ [~, ~, ber_ffe(i), ~] = calc_ber(Eq_bits.signal, Bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
+ fprintf('FFE: %.2e \n',ber_ffe(i));
+
+ % Postfilter
+ [y_white,~] = pf_.process(y_ffe, ffe_noise);
+
+ % Sequence Est
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
+ [y_mlse] = mlse_.process(y_white,Symbols);
+ mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
+ [~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
+ fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i));
% ML-base MLSE L=2
adaptive_mu = 0;
diff --git "a/projects/Messung_Z\303\274rich/minimal_matched.m" "b/projects/Messung_Z\303\274rich/minimal_matched.m"
new file mode 100644
index 0000000..a5dfa33
--- /dev/null
+++ "b/projects/Messung_Z\303\274rich/minimal_matched.m"
@@ -0,0 +1,60 @@
+L = 4; % Oversampling factor
+fsym = 1e9/4;
+rollOff = 0.5; % Pulse shaping roll-off factor
+htx = rcosdesign(rollOff,16, L, 'sqrt');
+hrx = conj(fliplr(htx));
+
+signal = zeros(1000,1);
+signal(500) = 1;
+Digi_sig = Informationsignal(signal,"fs",fsym);
+
+% Digi_sig = Digi_sig.resample("fs_out",L*fsym);
+%
+% Digi_sig_tx = Digi_sig.filter(htx,1);
+% Digi_sig_rx = Digi_sig_tx.filter(hrx,1);
+%
+% figure;plot(Digi_sig_tx.signal);hold on;plot(Digi_sig_rx.signal),plot(Digi_sig.signal);
+%
+%
+%
+%
+pulsef = Pulseformer("alpha",1,"matched",0,"fdac",Digi_sig.fs*L,"fsym",fsym,"pulse","rrc","pulselength",16);
+Digi_sig = pulsef.process(Digi_sig);
+
+pulsef = Pulseformer("alpha",1,"matched",1,"fdac",Digi_sig.fs,"fsym",fsym,"pulse","rrc","pulselength",16);
+Digi_sig_matched = pulsef.process(Digi_sig);
+
+% Filter:
+htx = rcosdesign(rollOff,16, L, 'sqrt');
+% Note half of the target delay is used, because when combined
+% to the matched filter, the total delay will be achieved.
+hrx = conj(fliplr(htx));
+
+figure
+plot(htx)
+title('Transmit Filter')
+xlabel('Index')
+ylabel('Amplitude')
+
+figure
+plot(hrx)
+title('Rx Filter (Matched Filter)')
+xlabel('Index')
+ylabel('Amplitude')
+
+p = conv(htx,hrx);
+
+figure
+plot(p)
+title('Combined Tx-Rx = Raised Cosine')
+xlabel('Index')
+ylabel('Amplitude')
+
+% And let's highlight the zero-crossings
+zeroCrossings = NaN*ones(size(p));
+zeroCrossings(1:L:end) = 0;
+zeroCrossings((rcDelay)*L + 1) = NaN; % Except for the central index
+hold on
+plot(zeroCrossings, 'o')
+legend('RC Pulse', 'Zero Crossings')
+hold off
\ No newline at end of file
diff --git a/projects/WDM/WDM_model_10km_queue.m b/projects/WDM/WDM_model_10km_queue.m
index a41cb02..8aae2ea 100644
--- a/projects/WDM/WDM_model_10km_queue.m
+++ b/projects/WDM/WDM_model_10km_queue.m
@@ -312,7 +312,7 @@ for realiz = 1:s.num_realiz
Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
- "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1).process(Opt_sig_wdm_fib);
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",1,"useSingle",1).process(Opt_sig_wdm_fib);
% -------- Evaluate at intermediate distance (enqueue jobs) --------
if eval_ptr <= nEval && seg == eval_seg(eval_ptr)
diff --git a/test/bayesopt_ffe_tuning.m b/test/bayesopt_ffe_tuning.m
new file mode 100644
index 0000000..adedb3d
--- /dev/null
+++ b/test/bayesopt_ffe_tuning.m
@@ -0,0 +1,219 @@
+%% Bayesian Optimization for FFE Parameter Tuning
+% This script uses bayesopt to find optimal mu_dd and mu_tr values
+% that minimize BER for the FFE equalizer.
+
+clear; clc;
+
+%% Setup - Same as gpu_processing_dpfiber.m
+s.wavelengthplan = calcWavelengthPlan(4, 400e9, 1310);
+link_length = 10;
+s.pmd = 0.1;
+s.gamma = 0.0023;
+
+s.M = 4;
+fsym = 112e9;
+fdac = 2*fsym;
+fadc = 120000000000;
+s.random_key = 1;
+
+% Laser / Modulator
+vbias_rel = 0.5;
+u_pi = 4.6;
+vbias = -vbias_rel*u_pi;
+laser_linewidth = 0e6;
+
+duob_mode = db_mode.no_db;
+rcalpha = 0.05;
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
+
+s.chirpalpha = 0;
+s.p_launch = 3;
+s.p = "co";
+
+N = numel(s.wavelengthplan);
+
+switch s.p
+ case "co"
+ pol_rot = 100.*ones(1,N);
+ d_local = 0;
+end
+
+f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
+margin = 25e12;
+f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
+f_nyq = f_span/2;
+
+kover = 4;
+upsample_required = f_nyq./(fdac*kover/2);
+upsample_pow = 2^nextpow2(upsample_required);
+
+s.f_opt = fdac*kover*upsample_pow;
+s.f_opt_nyq = s.f_opt/2;
+
+s.rop = -8; % Fixed ROP for optimization
+
+%% Generate TX signals (run once)
+fprintf('Generating TX signals...\n');
+for l = 1:N
+ [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
+ "fsym",fsym,"M",s.M,"order",15,"useprbs",0, ...
+ "fs_out",fdac, ...
+ "applyclipping",0,"clipfactor",1.5, ...
+ "applypulseform",1,"pulseformer",Pform, ...
+ "randkey",s.random_key+l, ...
+ "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
+ ).process();
+
+ Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
+ "filterType",filtertypes.gaussian,"active",true);
+
+ El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
+ "bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
+ "H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
+
+ El_sig = El_sig.normalize("mode","oneone");
+ scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
+ El_sig = El_sig .* scaling;
+
+ Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
+ "lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
+ "linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
+
+ signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
+end
+
+%% WDM mux + launch
+Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
+ "lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
+
+Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
+
+%% Fiber propagation
+segment_length = 1;
+nSegments = link_length/segment_length;
+nSegments = round(nSegments);
+
+zdw = 1310;
+randomize_D = true;
+Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
+
+Opt_sig_wdm_fib = Opt_sig_wdm;
+fprintf('Running fiber propagation...\n');
+for seg = 1:nSegments
+ fprintf('Segment %d/%d\n', seg, nSegments);
+ Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",true).process(Opt_sig_wdm_fib);
+end
+
+%% Pre-process to get Rx_sig (do demux once)
+fprintf('Pre-processing receiver chain...\n');
+l = 1; % Use channel 1 for optimization
+
+Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
+ "fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_wdm_fib);
+
+Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",s.rop).process(Opt_sig_demux{l});
+
+PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
+ "nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
+
+rx_bwl = 100e9;
+PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
+ "filterType",filtertypes.butterworth,"active",true).process(PD_sig);
+
+Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
+Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
+ "delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
+ "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
+ "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
+
+Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
+
+[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
+Rx_sig = Scpe_cell{1};
+Rx_sig = Rx_sig.normalize("mode","rms");
+
+fprintf('Receiver pre-processing complete. Ready for optimization.\n\n');
+
+%% Define the objective function for bayesopt
+function ber = ffe_objective(params, Rx_sig, Symbols_l, Tx_bits_l, M, duob_mode)
+mu_dd = params.mu_dd;
+mu_tr = params.mu_tr;
+
+try
+ eq_ffe = FFE("epochs_tr", 5, "epochs_dd", 2, "len_tr", 2^13, ...
+ "mu_dd", mu_dd, "mu_tr", mu_tr, ...
+ "order", 50, "sps", 2, "decide", 0, ...
+ "adaption", adaption_method.nlms, "dd_mode", 1);
+
+ ffe_results = ffe(eq_ffe, M, Rx_sig, Symbols_l, Tx_bits_l, ...
+ "precode_mode", duob_mode, ...
+ 'showAnalysis', 0, ...
+ "postFFE", [], ...
+ "eth_style_symbol_mapping", 0);
+
+ ber = ffe_results.metrics.BER;
+
+ if ber == 0
+ ber = 1e-10;
+ end
+
+ if ~isfinite(ber)
+ ber = 0.5;
+ end
+
+ fprintf(' mu_dd=%.4e, mu_tr=%.4e -> BER=%.4e\n', mu_dd, mu_tr, ber);
+
+catch ME
+ fprintf(' mu_dd=%.4e, mu_tr=%.4e -> FAILED (%s)\n', mu_dd, mu_tr, ME.message);
+ ber = 0.5;
+end
+end
+
+%% Define optimizable variables
+mu_dd_var = optimizableVariable('mu_dd', [1e-5, 0.1], 'Transform', 'log');
+mu_tr_var = optimizableVariable('mu_tr', [1e-5, 0.1], 'Transform', 'log');
+
+%% Run Bayesian Optimization
+fprintf('========== Starting Bayesian Optimization ==========\n');
+fprintf('Optimizing mu_dd and mu_tr to minimize BER\n');
+fprintf('Search range: mu_dd=[1e-5, 0.1], mu_tr=[1e-5, 0.1]\n\n');
+
+objective_fn = @(params) ffe_objective(params, Rx_sig, Symbols{l}, Tx_bits{l}, s.M, duob_mode);
+
+results = bayesopt(objective_fn, [mu_dd_var, mu_tr_var], ...
+ 'MaxObjectiveEvaluations', 30, ...
+ 'AcquisitionFunctionName', 'expected-improvement-plus', ...
+ 'IsObjectiveDeterministic', false, ...
+ 'ExplorationRatio', 0.5, ...
+ 'Verbose', 1, ...
+ 'PlotFcn', []);
+
+%% Display Results
+fprintf('\n========== FFE Optimization Complete ==========\n');
+fprintf('Best FFE parameters found:\n');
+fprintf(' mu_dd = %.6e\n', results.XAtMinObjective.mu_dd);
+fprintf(' mu_tr = %.6e\n', results.XAtMinObjective.mu_tr);
+fprintf(' BER = %.6e\n', results.MinObjective);
+
+%% Verify with optimal parameters
+fprintf('\nVerifying optimal FFE parameters...\n');
+best_mu_dd = results.XAtMinObjective.mu_dd;
+best_mu_tr = results.XAtMinObjective.mu_tr;
+
+eq_ffe_best = FFE("epochs_tr", 5, "epochs_dd", 2, "len_tr", 2^13, ...
+ "mu_dd", best_mu_dd, "mu_tr", best_mu_tr, ...
+ "order", 50, "sps", 2, "decide", 0, ...
+ "adaption", adaption_method.nlms, "dd_mode", 1);
+
+ffe_results_best = ffe(eq_ffe_best, s.M, Rx_sig, Symbols{l}, Tx_bits{l}, ...
+ "precode_mode", duob_mode, ...
+ 'showAnalysis', 1, ...
+ "postFFE", [], ...
+ "eth_style_symbol_mapping", 0);
+
+fprintf('\nFinal FFE BER with optimal parameters: %.6e\n', ffe_results_best.metrics.BER);
diff --git a/test/bitwise_demapping_pam6.m b/test/bitwise_demapping_pam6.m
new file mode 100644
index 0000000..6ea0b3d
--- /dev/null
+++ b/test/bitwise_demapping_pam6.m
@@ -0,0 +1,43 @@
+
+
+%%%%% SETTINGS %%%%%%
+useprbs = 1;
+M = 8;
+randkey = 1;
+fsym = 112e9;
+viewresults = 0;
+
+%%%%% Mapping %%%%%
+M = 6;
+data = 0:M-1;
+bitpersymbol = log2(M);
+
+s = RandStream('twister','Seed',1);
+bitpattern = randi(s,[0 1], 2^18, 1);
+bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
+
+bits_tx = Informationsignal(bitpattern);
+
+symbols = PAMmapper(M,0).map(bits);
+
+pam6transitions = combvec(PAMmapper(M,0).levels,PAMmapper(M,0).levels)';
+
+pam6bits = PAMmapper(6,0,"eth_style",0).demap(reshape(pam6transitions',[],1)./sqrt(10));
+symbols_rx = PAMmapper(M,0).map(pam6bits).*sqrt(10);
+
+pam6bits = reshape(pam6bits',5,[])';
+
+figure; hold on
+scatter(pam6transitions(:,1), pam6transitions(:,2), 'x', 'LineWidth', 1);
+n = size(pam6transitions,1);
+labels = cellstr(char(pam6bits + '0')); % -> N x 1 cell array of char rows
+text(pam6transitions(:,1), pam6transitions(:,2), labels, ...
+ 'HorizontalAlignment','left', 'VerticalAlignment','bottom');
+
+
+
+bits_rx = PAMmapper(M,0).demap(symbols);
+
+[~,error_num,ber,error_pos] = calc_ber(bits_tx.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
+
+PAMmapper(8,0).showBitMapping
\ No newline at end of file
diff --git a/test/duobinary_minimal_example.m b/test/duobinary_minimal_example.m
index d61c044..c39183e 100644
--- a/test/duobinary_minimal_example.m
+++ b/test/duobinary_minimal_example.m
@@ -29,9 +29,6 @@ para.skip =0;
para.bruijn = 0;
para.reset_prms = 0;
para.method = 1;
-para.pcs = 0;
-para.shape_para = 0;
-para.rng_num = 0;
data_in = [];
global loop;
diff --git a/test/gpu_cpu_comparison.m b/test/gpu_cpu_comparison.m
new file mode 100644
index 0000000..02d40ba
--- /dev/null
+++ b/test/gpu_cpu_comparison.m
@@ -0,0 +1,349 @@
+%% GPU vs CPU Comparison Test for DP_Fiber
+% This script runs the fiber simulation with and without GPU acceleration
+% and compares the numerical results.
+
+% clear; clc;
+
+%% Setup (same as gpu_processing_dpfiber.m but simplified)
+s.wavelengthplan = calcWavelengthPlan(4, 400e9, 1310);
+link_length = 10;
+s.pmd = 0.1;
+s.gamma = 0.0023;
+
+s.M = 4;
+fsym = 112e9;
+fdac = 2*fsym;
+fadc = 120000000000;
+s.random_key = 1;
+
+% Laser / Modulator
+vbias_rel = 0.5;
+u_pi = 4.6;
+vbias = -vbias_rel*u_pi;
+laser_linewidth = 0e6;
+
+% DB Stuff
+duob_mode = db_mode.no_db;
+
+rcalpha = 0.05;
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
+
+s.chirpalpha = 0;
+s.p_launch = 3;
+s.p = "co";
+
+N = numel(s.wavelengthplan);
+
+switch s.p
+ case "co"
+ pol_rot = 100.*ones(1,N);
+ d_local = 0;
+ case "pair"
+ pol_rot = repmat([100,100,0,0],1,N/4);
+ d_local = 0;
+ case "alt"
+ pol_rot = repmat([100,0,100,0],1,N/4);
+ d_local = 0;
+ case "seg"
+ pol_rot = 100.*ones(1,N);
+ d_local = 3;
+ otherwise
+ error('Unknown fwm_mitigation_technique: %s', string(s.p));
+end
+
+f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
+margin = 5e12;
+f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
+f_nyq = f_span/2;
+
+kover = 4;
+upsample_required = f_nyq./(fdac*kover/2);
+upsample_pow = 2^nextpow2(upsample_required);
+
+s.f_opt = fdac*kover*upsample_pow;
+s.f_opt_nyq = s.f_opt/2;
+
+%% TX per channel
+for l = 1:N
+ [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
+ "fsym",fsym,"M",s.M,"order",15,"useprbs",0, ...
+ "fs_out",fdac, ...
+ "applyclipping",0,"clipfactor",1.5, ...
+ "applypulseform",1,"pulseformer",Pform, ...
+ "randkey",s.random_key+l, ...
+ "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
+ ).process();
+
+ Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
+ "filterType",filtertypes.gaussian,"active",true);
+
+ El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
+ "bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
+ "H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
+
+ clear Digi_sig
+
+ El_sig = El_sig.normalize("mode","oneone");
+ scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
+ El_sig = El_sig .* scaling;
+
+ Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
+ "lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
+ "linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
+
+ clear El_sig
+
+ signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
+
+ clear Eml_out Lp_awg
+end
+
+disp('Signal generated for all channels.');
+
+%% WDM mux + launch
+Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
+ "lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
+
+Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
+
+% Save input for both runs
+Opt_sig_input = Opt_sig_wdm;
+
+segment_length = 1;
+nSegments = link_length/segment_length;
+if abs(nSegments - round(nSegments)) > 1e-12
+ error('fiber_length_km=%g must be an integer multiple of segment_length=%g km.', link_length, segment_length);
+end
+nSegments = round(nSegments);
+
+zdw = 1310;
+randomize_D = true;
+
+if nSegments > 0
+ Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
+else
+ Dvec = [];
+end
+
+%% Run WITHOUT GPU
+fprintf('\n========== Running WITHOUT GPU (CPU) ==========\n');
+Opt_sig_cpu = Opt_sig_input;
+
+tic;
+for seg = 1:nSegments
+ fprintf('CPU Segment %d/%d \n',seg, nSegments);
+
+ Opt_sig_cpu = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",false).process(Opt_sig_cpu);
+end
+time_cpu = toc;
+fprintf('CPU Time: %.3f seconds\n', time_cpu);
+
+%% Run WITH GPU (Double Precision)
+fprintf('\n========== Running WITH GPU (Double Precision) ==========\n');
+Opt_sig_gpu_double = Opt_sig_input;
+
+tic;
+for seg = 1:nSegments
+ fprintf('GPU-Double Segment %d/%d \n',seg, nSegments);
+
+ Opt_sig_gpu_double = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",false).process(Opt_sig_gpu_double);
+end
+time_gpu_double = toc;
+fprintf('GPU Double Time: %.3f seconds\n', time_gpu_double);
+
+%% Run WITH GPU (Single Precision)
+fprintf('\n========== Running WITH GPU (Single Precision) ==========\n');
+Opt_sig_gpu_single = Opt_sig_input;
+
+tic;
+for seg = 1:nSegments
+ fprintf('GPU-Single Segment %d/%d \n',seg, nSegments);
+
+ Opt_sig_gpu_single = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",true).process(Opt_sig_gpu_single);
+end
+time_gpu_single = toc;
+fprintf('GPU Single Time: %.3f seconds\n', time_gpu_single);
+
+%% Compare Results
+fprintf('\n========== Numerical Comparison ==========\n');
+
+sig_cpu = Opt_sig_cpu.signal;
+sig_gpu_double = Opt_sig_gpu_double.signal;
+sig_gpu_single = Opt_sig_gpu_single.signal;
+
+% Check dimensions
+fprintf('CPU signal size: [%d x %d]\n', size(sig_cpu,1), size(sig_cpu,2));
+fprintf('GPU Double signal size: [%d x %d]\n', size(sig_gpu_double,1), size(sig_gpu_double,2));
+fprintf('GPU Single signal size: [%d x %d]\n', size(sig_gpu_single,1), size(sig_gpu_single,2));
+
+% CPU vs GPU Double
+fprintf('\n--- CPU vs GPU Double ---\n');
+diff_cpu_double = abs(sig_cpu - sig_gpu_double);
+max_diff_cpu_double = max(diff_cpu_double(:));
+mean_diff_cpu_double = mean(diff_cpu_double(:));
+rel_diff_cpu_double = max_diff_cpu_double / max(abs(sig_cpu(:)));
+fprintf('Max absolute difference: %.6e\n', max_diff_cpu_double);
+fprintf('Mean absolute difference: %.6e\n', mean_diff_cpu_double);
+fprintf('Max relative difference: %.6e\n', rel_diff_cpu_double);
+
+% CPU vs GPU Single
+fprintf('\n--- CPU vs GPU Single ---\n');
+diff_cpu_single = abs(sig_cpu - sig_gpu_single);
+max_diff_cpu_single = max(diff_cpu_single(:));
+mean_diff_cpu_single = mean(diff_cpu_single(:));
+rel_diff_cpu_single = max_diff_cpu_single / max(abs(sig_cpu(:)));
+fprintf('Max absolute difference: %.6e\n', max_diff_cpu_single);
+fprintf('Mean absolute difference: %.6e\n', mean_diff_cpu_single);
+fprintf('Max relative difference: %.6e\n', rel_diff_cpu_single);
+
+% GPU Double vs GPU Single
+fprintf('\n--- GPU Double vs GPU Single ---\n');
+diff_double_single = abs(sig_gpu_double - sig_gpu_single);
+max_diff_double_single = max(diff_double_single(:));
+mean_diff_double_single = mean(diff_double_single(:));
+rel_diff_double_single = max_diff_double_single / max(abs(sig_gpu_double(:)));
+fprintf('Max absolute difference: %.6e\n', max_diff_double_single);
+fprintf('Mean absolute difference: %.6e\n', mean_diff_double_single);
+fprintf('Max relative difference: %.6e\n', rel_diff_double_single);
+
+% Check tolerances
+fprintf('\n--- Tolerance Check ---\n');
+tol_double = 1e-10;
+tol_single = 1e-5; % Single precision has ~7 significant digits
+
+if max_diff_cpu_double < tol_double
+ fprintf('✓ CPU vs GPU Double: EQUIVALENT (diff < %.0e)\n', tol_double);
+else
+ fprintf('✗ CPU vs GPU Double: DIFFER beyond tolerance (%.0e)\n', tol_double);
+end
+
+if max_diff_cpu_single < tol_single
+ fprintf('✓ CPU vs GPU Single: ACCEPTABLE (diff < %.0e)\n', tol_single);
+else
+ fprintf('⚠ CPU vs GPU Single: Precision loss detected (diff = %.2e, tol = %.0e)\n', max_diff_cpu_single, tol_single);
+end
+
+% Performance comparison
+fprintf('\n========== Performance Summary ==========\n');
+fprintf('CPU Time: %.3f s\n', time_cpu);
+fprintf('GPU Double Time: %.3f s\n', time_gpu_double);
+fprintf('GPU Single Time: %.3f s\n', time_gpu_single);
+fprintf('\n');
+fprintf('Speedup (GPU Double vs CPU): %.2fx\n', time_cpu/time_gpu_double);
+fprintf('Speedup (GPU Single vs CPU): %.2fx\n', time_cpu/time_gpu_single);
+fprintf('Speedup (GPU Single vs GPU Double): %.2fx\n', time_gpu_double/time_gpu_single);
+
+%% ========== BER Comparison ==========
+% Process each fiber output through simplified receiver to check if
+% single-precision affects actual BER performance
+
+fprintf('\n========== BER Comparison ==========\n');
+fprintf('Processing signals through receiver chain...\n');
+
+% Receiver parameters
+rop = -7; % Received optical power [dBm]
+len_tr = 4096; % Training length
+mu_dc = 0.005;
+mu_ffe = [0.0001 0.0008 0.001];
+mu_dfe = 0.0004;
+
+% Helper function to process through receiver and get BER
+function ber = process_receiver(Opt_sig_fib, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode)
+
+% Demux single channel
+Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
+ "fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_fib);
+
+% ROP amplifier
+Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",rop).process(Opt_sig_demux{l});
+
+% Photodiode
+PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
+ "nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
+
+% Low-pass filter
+rx_bwl = 100e9;
+PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
+ "filterType",filtertypes.butterworth,"active",true).process(PD_sig);
+
+% Scope
+Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
+Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
+ "delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
+ "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
+ "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
+
+% Resample to 2 sps
+Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
+
+% Time sync
+[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
+Rx_sig = Scpe_cell{1};
+Rx_sig = Rx_sig.normalize("mode","rms");
+
+% FFE Equalizer
+ffe_order = [50, 0, 0];
+eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
+
+ffe_results = ffe(eq_ffe,s.M,Rx_sig,Symbols{l},Tx_bits{l}, ...
+ "precode_mode",duob_mode, ...
+ 'showAnalysis',0, ...
+ "postFFE",[], ...
+ "eth_style_symbol_mapping",0);
+
+ber = ffe_results.metrics.BER;
+end
+
+% Process each mode for channel 1
+l = 4; % Use first channel for comparison
+
+fprintf('Processing CPU result...\n');
+ber_cpu = process_receiver(Opt_sig_cpu, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
+
+fprintf('Processing GPU Double result...\n');
+ber_gpu_double = process_receiver(Opt_sig_gpu_double, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
+
+fprintf('Processing GPU Single result...\n');
+ber_gpu_single = process_receiver(Opt_sig_gpu_single, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
+
+% Display BER results
+fprintf('\n========== BER Results (Channel %d, ROP = %d dBm) ==========\n', l, rop);
+fprintf('CPU: BER = %.4e\n', ber_cpu);
+fprintf('GPU Double: BER = %.4e\n', ber_gpu_double);
+fprintf('GPU Single: BER = %.4e\n', ber_gpu_single);
+
+fprintf('\n--- BER Comparison ---\n');
+if ber_cpu == 0 && ber_gpu_double == 0 && ber_gpu_single == 0
+ fprintf('✓ All BERs are zero (no errors detected)\n');
+else
+ ber_diff_double = abs(ber_cpu - ber_gpu_double);
+ ber_diff_single = abs(ber_cpu - ber_gpu_single);
+ fprintf('|BER_cpu - BER_gpu_double| = %.4e\n', ber_diff_double);
+ fprintf('|BER_cpu - BER_gpu_single| = %.4e\n', ber_diff_single);
+
+ if ber_diff_double < 1e-6 && ber_diff_single < 1e-6
+ fprintf('✓ BER differences are negligible\n');
+ elseif ber_diff_single > ber_diff_double * 10
+ fprintf('⚠ Single precision shows measurable BER impact\n');
+ else
+ fprintf('✓ BER differences within acceptable range\n');
+ end
+end
+
+fprintf('\n========== Test Complete ==========\n');
diff --git a/test/gpu_processing_dpfiber.m b/test/gpu_processing_dpfiber.m
new file mode 100644
index 0000000..317833e
--- /dev/null
+++ b/test/gpu_processing_dpfiber.m
@@ -0,0 +1,265 @@
+
+
+s.wavelengthplan = calcWavelengthPlan(16, 400e9, 1310);
+N = numel(s.wavelengthplan);
+link_length = 10;
+s.pmd = 0.1;%0.1;
+s.gamma = 0.0023;
+
+s.M = 4;
+fsym = 112e9;
+fdac = 2*fsym;
+fadc = 120000000000;
+s.random_key = 1;
+
+% Laser / s.Modulator
+vbias_rel = 0.5;
+u_pi = 4.6;
+vbias = -vbias_rel*u_pi;
+laser_linewidth = 0e6;
+
+% DB Stuff
+duob_mode = db_mode.no_db;
+
+rcalpha = 0.05;
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
+
+s.chirpalpha = 0;
+
+s.p_launch = 3;
+s.p = "co";
+
+switch s.p
+ case "co"
+ pol_rot = 100.*ones(1,N);
+ d_local = 0;
+ case "pair"
+ pol_rot = repmat([100,100,0,0],1,N/4);
+ d_local = 0;
+ case "alt"
+ pol_rot = repmat([100,0,100,0],1,N/4);
+ d_local = 0;
+ case "seg"
+ pol_rot = 100.*ones(1,N);
+ d_local = 3;
+ otherwise
+ error('Unknown fwm_mitigation_technique: %s', string(s.p));
+end
+
+f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
+margin = 25e12; % some THz left and right
+f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
+f_nyq = f_span/2;
+
+kover = 4;
+upsample_required = f_nyq./(fdac*kover/2);
+upsample_pow = 2^nextpow2(upsample_required);
+
+s.f_opt = fdac*kover*upsample_pow;
+s.f_opt_nyq = s.f_opt/2;
+
+s.rop = -10:1:0;
+
+profile on
+
+%% ---------- TX per channel ----------
+for l = 1:N
+
+ [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
+ "fsym",fsym,"M",s.M,"order",17,"useprbs",0, ...
+ "fs_out",fdac, ...
+ "applyclipping",0,"clipfactor",1.5, ...
+ "applypulseform",1,"pulseformer",Pform, ...
+ "randkey",s.random_key+l, ...
+ "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
+ ).process();
+
+ Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
+ "filterType",filtertypes.gaussian,"active",true);
+
+ El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
+ "bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
+ "H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
+
+ % Digi_sig not needed after AWG
+ clear Digi_sig
+
+ % Electrical Driver Amplifier
+ El_sig = El_sig.normalize("mode","oneone");
+ scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
+ El_sig = El_sig .* scaling;
+
+ % E/O Conversion
+ Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
+ "lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
+ "linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
+
+ % El_sig not needed after EML
+ clear El_sig
+
+ signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
+
+ % Eml_out not needed after pol controller
+ clear Eml_out Lp_awg
+end
+
+disp('Signal generated for all channels.');
+
+%% ---------- WDM mux + launch ----------
+Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
+ "lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
+
+Opt_sig_wdm.spectrum();
+
+Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
+
+Opt_sig_wdm_fib = Opt_sig_wdm;
+
+segment_length = 1;
+nSegments = link_length/segment_length;
+if abs(nSegments - round(nSegments)) > 1e-12
+ error('fiber_length_km=%g must be an integer multiple of segment_length=%g km.', link_length, segment_length);
+end
+nSegments = round(nSegments);
+
+zdw = 1310;
+randomize_D = true;
+
+% Guard for 0 km: avoid calling getDispersionVector(0,...) if it doesn't support it
+if nSegments > 0
+ Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
+else
+ Dvec = [];
+end
+
+for seg = 1:nSegments
+
+ fprintf('Segment %d/%d \n',seg, nSegments);
+
+ Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",1).process(Opt_sig_wdm_fib);
+
+end
+
+profile off
+profile viewer
+
+Opt_sig_wdm_fib.spectrum();
+
+%% ========== BER Evaluation ==========
+fprintf('\n========== BER Evaluation ==========\n');
+fprintf('Processing signals through receiver chain...\n');
+
+% Receiver parameters
+len_tr = 4096; % Training length
+mu_dc = 0.005;
+mu_ffe = [0.0001 0.0008 0.001];
+mu_dfe = 0.0004;
+
+% Helper function to process through receiver and get BER
+function ber = process_receiver(Opt_sig_fib, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode)
+
+% Demux single channel
+Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
+ "fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_fib);
+
+% ROP amplifier
+Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",rop).process(Opt_sig_demux{l});
+
+% Photodiode
+PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
+ "nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
+
+% Low-pass filter
+rx_bwl = 100e9;
+PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
+ "filterType",filtertypes.butterworth,"active",true).process(PD_sig);
+
+% Scope
+Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
+Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
+ "delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
+ "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
+ "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
+
+% Resample to 2 sps
+Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
+
+% Time sync
+[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
+Rx_sig = Scpe_cell{1};
+Rx_sig = Rx_sig.normalize("mode","rms");
+
+% FFE Equalizer
+ffe_order = [50, 0, 0];
+eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
+eq_ffe = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",6.624e-05,"mu_tr",0.058136,"order",50,"sps",2,"decide",0, "adaption",adaption_method.nlms,"dd_mode",1);
+
+ffe_results = ffe(eq_ffe,s.M,Rx_sig,Symbols{l},Tx_bits{l}, ...
+ "precode_mode",duob_mode, ...
+ 'showAnalysis',0, ...
+ "postFFE",[], ...
+ "eth_style_symbol_mapping",0);
+
+ber = ffe_results.metrics.BER;
+end
+
+% Process each ROP value for selected channels using parfor
+ber_results = zeros(length(s.rop), N);
+
+% Flatten loop for parfor: iterate over all (ROP, channel) combinations
+num_rop = length(s.rop);
+rop_vals = s.rop;
+ber_flat = zeros(num_rop * N, 1);
+
+parfor idx = 1:(num_rop * N)
+ % Convert linear index to (ri, l) subscripts
+ ri = ceil(idx / N);
+ l = mod(idx - 1, N) + 1;
+
+ fprintf('ROP %d dBm, Channel %d/%d\n', rop_vals(ri), l, N);
+ ber_flat(idx) = process_receiver(Opt_sig_wdm_fib, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop_vals(ri), s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
+end
+
+% Reshape back to [num_rop × N] matrix
+ber_results = reshape(ber_flat, [N, num_rop]).';
+
+
+%% Display BER Results
+fprintf('\n========== BER Results ==========\n');
+fprintf('ROP [dBm] | ');
+for l = 1:N
+ fprintf('Ch%d | ', l);
+end
+fprintf('\n');
+
+for ri = 1:length(s.rop)
+ fprintf('%8d | ', s.rop(ri));
+ for l = 1:N
+ fprintf('%.2e | ', ber_results(ri, l));
+ end
+ fprintf('\n');
+end
+
+% Plot BER vs ROP
+figure;
+semilogy(s.rop, mean(ber_results, 2), '-o', 'LineWidth', 2);
+hold on;
+for l = 1:N
+ semilogy(s.rop, ber_results(:, l), '--', 'LineWidth', 1);
+end
+hold off;
+xlabel('ROP [dBm]');
+ylabel('BER');
+title('BER vs Received Optical Power (GPU Single Precision)');
+legend(['Mean', arrayfun(@(x) sprintf('Ch%d', x), 1:N, 'UniformOutput', false)]);
+grid on;
+
+fprintf('\n========== Test Complete ==========\n');
diff --git a/test/gpu_processing_test.m b/test/gpu_processing_test.m
new file mode 100644
index 0000000..b08c03f
--- /dev/null
+++ b/test/gpu_processing_test.m
@@ -0,0 +1,70 @@
+% 1. Setup Data OUTSIDE the timer
+d = gpuDevice;
+N = 10000;
+
+fprintf('Preparing data...\n');
+A_cpu_double = rand(N, N); % Create double on CPU
+A_cpu_single = single(A_cpu_double); % Create single on CPU
+
+% Warmup run (wakes up the GPU from idle state)
+A_warm = gpuArray.rand(1000, 1000, 'single');
+B_warm = A_warm * A_warm;
+wait(d);
+
+fprintf('------------------------------------------------\n');
+
+% TEST 1: Double Precision (The "Slow" way)
+% We move data to GPU first so we only measure calculation time
+A_gpu_double = gpuArray(A_cpu_double);
+wait(d); % Ensure transfer is done before starting timer
+
+fprintf('Running DOUBLE precision test... ');
+tic;
+B_gpu = A_gpu_double * A_gpu_double;
+wait(d); % FORCE MATLAB TO WAIT FOR GPU
+time_double = toc;
+fprintf('Done.\n');
+fprintf('Double Precision Time: %.4f seconds\n', time_double);
+
+% TEST 2: Single Precision (The "Fast" way)
+A_gpu_single = gpuArray(A_cpu_single);
+wait(d); % Ensure transfer is done
+
+fprintf('Running SINGLE precision test... ');
+tic;
+B_gpu = A_gpu_single * A_gpu_single;
+wait(d); % FORCE MATLAB TO WAIT FOR GPU
+time_single = toc;
+fprintf('Done.\n');
+fprintf('Single Precision Time: %.4f seconds\n', time_single);
+
+% Calculate Speedup
+fprintf('------------------------------------------------\n');
+fprintf('Speedup Factor using single precision: %.2fx\n', time_double / time_single);
+
+%
+% Create 10,000 small matrices (10x10) stacked in a 3D array
+A_stack = gpuArray.rand(10, 10, 10000, 'single');
+B_stack = gpuArray.rand(10, 10, 10000, 'single');
+
+% BAD: Looping (GPU overhead kills you)
+tic;
+for i=1:10000
+ C(:,:,i) = A_stack(:,:,i) * B_stack(:,:,i);
+end
+wait(d);
+loop_time = toc;
+
+% GOOD: Pagefun (Executes all 10,000 mults simultaneously)
+tic;
+C_stack = pagefun(@mtimes, A_stack, B_stack);
+wait(d);
+pagefun_time = toc;
+
+fprintf('------------------------------------------------\n');
+fprintf('Speedup Factor using pagefun: %.2fx\n', loop_time / pagefun_time);
+
+
+fprintf('Total VRAM: %.2f GB\n', d.TotalMemory / 1e9);
+fprintf('Available VRAM: %.2f GB\n', d.AvailableMemory / 1e9);
+fprintf('Usage: %.1f%%\n', 100 * (1 - d.AvailableMemory / d.TotalMemory));
\ No newline at end of file
index e06c41f..f09a03e 100644
--- a/Classes/00_signals/Signal.m
+++ b/Classes/00_signals/Signal.m
@@ -172,11 +172,9 @@ classdef Signal
hold on;
if isempty(options.color)
- % plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Marker', '.', 'LineStyle','none', 'MarkerSize', 0.1);
- plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1);
+ plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Marker', '.', 'LineStyle','none', 'MarkerSize', 0.1);
else
- % plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Marker', '.', 'LineStyle','none', 'MarkerSize', 0.1,'Color',options.color);
- plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Color',options.color);
+ plot(t* 1e6, sig(1:length(t)), 'DisplayName', dn, 'LineWidth', 0.1, 'Marker', '.', 'LineStyle','none', 'MarkerSize', 0.1,'Color',options.color);
end
% 2 c)
% - xlabel if not already here: time in readable format (1 ms and not 1e-3 s)
diff --git a/Classes/02_optical/DP_Fiber.m b/Classes/02_optical/DP_Fiber.m
index a5f3dce..c1f08ea 100644
--- a/Classes/02_optical/DP_Fiber.m
+++ b/Classes/02_optical/DP_Fiber.m
@@ -24,6 +24,8 @@ classdef DP_Fiber
SS_dzmax % [m] max dz (adaptive SSFM)
SS_dzmin % [m] min dz (adaptive SSFM)
n_waveplates % number of PMD waveplates
+ useGPU % GPU acceleration: true, false, or 'auto' (default)
+ useSingle % Use single precision on GPU (default: false)
% ---- Internal state (persistent between calls) ----
state % struct mirroring legacy 'state'
@@ -56,6 +58,8 @@ classdef DP_Fiber
options.SS_dzmax = 2e4 % m
options.SS_dzmin = 100 % m
options.n_waveplates = 100
+ options.useGPU = 'auto' % 'auto', true, or false
+ options.useSingle = false % single precision GPU
end
% Copy provided options into properties
@@ -208,7 +212,7 @@ classdef DP_Fiber
% Frequency-dependent PMD phase term (legacy form)
st.brf.db0 = (R.rand(st.wave_plates,1)*2*pi - pi) * brf_multiplier;
st.brf.db1 = sqrt(3*pi/8)*(st.dgd/obj.fa)/st.wave_plates .* st.omega;
- st.brf.simdgd = 0;
+ st.brf.simdgd = 0;
% cumsum used in legacy only for debug; keep compatibility variable:
~cumsum(st.brf.db0); % no-op to mirror legacy path
@@ -228,7 +232,18 @@ classdef DP_Fiber
x_in = signal_in(:,1).';
y_in = signal_in(:,2).';
- [x_out, y_out, obj.state] = CNLSE_plain(x_in, y_in, obj.state);
+ % Determine GPU usage
+ if ischar(obj.useGPU) || isstring(obj.useGPU)
+ if strcmpi(obj.useGPU, 'auto')
+ gpuFlag = []; % Let CNLSE_plain auto-detect
+ else
+ error('DP_Fiber:InvalidGPU', 'useGPU must be true, false, or ''auto''');
+ end
+ else
+ gpuFlag = logical(obj.useGPU);
+ end
+
+ [x_out, y_out, obj.state] = CNLSE_plain(x_in, y_in, obj.state, gpuFlag, obj.useSingle);
obj.state.propagated_length = obj.state.propagated_length + obj.state.L;
diff --git a/Classes/02_optical/Optical_Demultiplex.m b/Classes/02_optical/Optical_Demultiplex.m
index 7d7c39e..8e272d2 100644
--- a/Classes/02_optical/Optical_Demultiplex.m
+++ b/Classes/02_optical/Optical_Demultiplex.m
@@ -42,7 +42,7 @@ classdef Optical_Demultiplex < handle
function signalclasses_out = process(obj, signalclass_in)
- % ---- Infer wavelength: either given or from input total signal
+ % ---- Infer wavelength: either given or from input total signal
if isempty(obj.wavelengthplan)
obj.wavelengthplan = signalclass_in.lambda; %meter
else
@@ -81,7 +81,7 @@ classdef Optical_Demultiplex < handle
obj
signal_in
end
-
+
w = obj.fs_out ./ obj.fs_in ;
blocklen_in = length(signal_in);
blocklen_out = w*blocklen_in;
@@ -119,30 +119,31 @@ classdef Optical_Demultiplex < handle
N = size(lo,1);
C = size(lo,2);
- x_envelopes = zeros(N, C, 'like', signal_in);
- y_envelopes = zeros(N, C, 'like', signal_in);
+ % ---- VECTORIZED: Process all channels in parallel ----
+ % Batched FFT operates on each column simultaneously on GPU
- s1 = signal_in(:,1);
- s2 = signal_in(:,2);
+ % Extract polarization signals
+ s1 = signal_in(:,1); % X polarization [N×1]
+ s2 = signal_in(:,2); % Y polarization [N×1]
- % Reusable work buffers (avoid reallocations)
- wrk_time = zeros(N,1, 'like', signal_in);
- wrk_freq = zeros(N,1, 'like', signal_in);
+ % Broadcast signal to all channels and multiply with LO
+ % s1, s2 are [N×1], lo is [N×C] → result is [N×C]
+ x_mixed = att .* s1 .* lo; % [N×C]
+ y_mixed = att .* s2 .* lo; % [N×C]
+
+ % Batched FFT: each column computed in parallel
+ x_freq = fft(x_mixed); % [N×C]
+ y_freq = fft(y_mixed); % [N×C]
+
+ % Apply filter (H is [N×1], broadcasts across columns)
+ x_filtered = x_freq .* H; % [N×C]
+ y_filtered = y_freq .* H; % [N×C]
+
+ % Batched IFFT
+ x_envelopes = ifft(x_filtered); % [N×C]
+ y_envelopes = ifft(y_filtered); % [N×C]
- for c = 1:C
- % ---- X branch ----
- wrk_time(:) = att .* s1 .* lo(:,c); % N×1
- wrk_freq(:) = fft(wrk_time); % N×1
- wrk_freq(:) = wrk_freq .* H; % N×1
- x_envelopes(:,c) = ifft(wrk_freq); % N×1
- % ---- Y branch ----
- wrk_time(:) = att .* s2 .* lo(:,c);
- wrk_freq(:) = fft(wrk_time);
- wrk_freq(:) = wrk_freq .* H;
- y_envelopes(:,c) = ifft(wrk_freq);
- end
-
end
end
end
diff --git a/Classes/02_optical/Optical_Multiplex.m b/Classes/02_optical/Optical_Multiplex.m
index 1d722b0..7fc8a33 100644
--- a/Classes/02_optical/Optical_Multiplex.m
+++ b/Classes/02_optical/Optical_Multiplex.m
@@ -1,10 +1,10 @@
classdef Optical_Multiplex < handle
% Takes a cell array of signals
- % returns a total field signal
- % WDM spacing is given in wavelength plan OR via delta_F
-
+ % returns a total field signal
+ % WDM spacing is given in wavelength plan OR via delta_F
+
% The grid is stored in the output signal -> the demux will ideally
- % look this up and use this as the demux frequencies...
+ % look this up and use this as the demux frequencies...
% signal_cell = {Opt_sig_1, Opt_sig_2};
% Opt_sig_wdm = Optical_Multiplex("fs_in",Opt_sig.fs,"fs_out",4*Opt_sig.fs,...
@@ -117,7 +117,7 @@ classdef Optical_Multiplex < handle
% adapt frequency shifts to match the FFT grid! Find nearest grid point
[glitch(o),pos] = min(abs( freqaxis-obj.df_T(o) ));
obj.df_T(o) = freqaxis(pos);
-
+
polrots = [polrots, data_in{o}.polrot];
end
@@ -139,25 +139,36 @@ classdef Optical_Multiplex < handle
x_envelopes = NaN([blocklen_out N]);
y_envelopes = x_envelopes;
-
- for o = 1:N
-
- pha = mod(2*pi*(0:blocklen_out-1)*obj.df_T(o)/obj.fs_out,2*pi).';
- lo = cos(pha)+1i*sin(pha);
- data_in_resampled = data_in{o}.resample("fs_out",obj.fs_out);
- res_env = ifft(fft(data_in_resampled.signal(:,1)).*H);
- x_envelopes(:,o) = att.*res_env.*lo;
+ % ---- OPTIMIZED: Pre-compute all LO phases as [blocklen_out × N] matrix ----
+ time_idx = (0:blocklen_out-1).'; % [blocklen_out × 1]
+ lo_phases = mod(2*pi * time_idx * obj.df_T / obj.fs_out, 2*pi); % [blocklen_out × N]
+ lo_all = cos(lo_phases) + 1i*sin(lo_phases); % [blocklen_out × N]
- res_env = ifft(fft(data_in_resampled.signal(:,2)).*H);
- y_envelopes(:,o) = att.*res_env.*lo;
+ % Collect all resampled signals first (still requires loop due to cell array)
+ x_signals = zeros(blocklen_out, N);
+ y_signals = zeros(blocklen_out, N);
+ for o = 1:N
+ data_in_resampled = data_in{o}.resample("fs_out", obj.fs_out);
+ x_signals(:, o) = data_in_resampled.signal(:, 1);
+ y_signals(:, o) = data_in_resampled.signal(:, 2);
end
+ % ---- VECTORIZED: Batched FFT/IFFT for all channels ----
+ % Apply filter to all channels at once
+ x_filtered = ifft(fft(x_signals) .* H); % [blocklen_out × N]
+ y_filtered = ifft(fft(y_signals) .* H); % [blocklen_out × N]
+
+ % Apply attenuation and LO shift to all channels
+ x_envelopes = att .* x_filtered .* lo_all; % [blocklen_out × N]
+ y_envelopes = att .* y_filtered .* lo_all; % [blocklen_out × N]
+
data_out = data_in_resampled;
data_out.signal = [sum(x_envelopes,2), sum(y_envelopes,2)];
data_out.lambda = obj.lambda_T;
data_out.polrot = polrots;
+
end
end
diff --git a/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m b/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m
index cfa7294..463adca 100644
--- a/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m
+++ b/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m
@@ -1,41 +1,72 @@
-function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
-
-
-
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- % pre calculations
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
- state.common_beta=struct('X',0,'Y',0);
-
- for n=1:2
- % get current polarization name and contrary one
- curPol = state.polNames{n};
+function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state,useGPU,useSingle)
+
+% GPU auto-detection if not specified
+if nargin < 4 || isempty(useGPU)
+ useGPU = canUseGPU();
+end
+if nargin < 5 || isempty(useSingle)
+ useSingle = false;
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% pre calculations
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+state.common_beta=struct('X',0,'Y',0);
+
+for n=1:2
+ % get current polarization name and contrary one
+ curPol = state.polNames{n};
+
+ % extend linear transfer function depending on beta values for the current polarization
+ % Was ist der Sinn dieser komischen beta notation? zB. state.beta.X = [0.3142 0 -9.1105e-28 5.1068e-41]
+ for n_beta = 1:length(state.beta.(curPol))
+ state.common_beta.(curPol) = state.common_beta.(curPol) + state.beta.(curPol)(n_beta) * (state.omega).^(n_beta-1) / factorial(n_beta-1);
+ end
- % extend linear transfer function depending on beta values for the current polarization
- % Was ist der Sinn dieser komischen beta notation? zB. state.beta.X = [0.3142 0 -9.1105e-28 5.1068e-41]
- for n_beta = 1:length(state.beta.(curPol))
- state.common_beta.(curPol) = state.common_beta.(curPol) + state.beta.(curPol)(n_beta) * (state.omega).^(n_beta-1) / factorial(n_beta-1);
+ %opt_out_struct.(curPol)=opt_in_struct.(curPol).envelope;
+end
+
+beta_const = state.beta.('X')(1);
+beta_1 = state.beta.('X')(2);
+beta_2 = state.beta.('X')(3);
+beta_3 = state.beta.('X')(4);
+deltaomega = state.omega;
+beta_x = beta_const + beta_1 * deltaomega + 1/2 * beta_2 * deltaomega.^2 + 1/6 *beta_3 * deltaomega.^3;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% GPU Transfer (if enabled)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+if useGPU
+ % Convert to single precision if requested (faster on most GPUs)
+ if useSingle
+ opt_in_x = gpuArray(single(opt_in_x));
+ opt_in_y = gpuArray(single(opt_in_y));
+ state.common_beta.X = gpuArray(single(state.common_beta.X));
+ state.common_beta.Y = gpuArray(single(state.common_beta.Y));
+ state.brf.db1 = gpuArray(single(state.brf.db1));
+ state.brf.db0 = gpuArray(single(state.brf.db0));
+ for k = 1:numel(state.brf.matR)
+ state.brf.matR{k} = gpuArray(single(state.brf.matR{k}));
+ end
+ else
+ opt_in_x = gpuArray(opt_in_x);
+ opt_in_y = gpuArray(opt_in_y);
+ state.common_beta.X = gpuArray(state.common_beta.X);
+ state.common_beta.Y = gpuArray(state.common_beta.Y);
+ state.brf.db1 = gpuArray(state.brf.db1);
+ state.brf.db0 = gpuArray(state.brf.db0);
+ for k = 1:numel(state.brf.matR)
+ state.brf.matR{k} = gpuArray(state.brf.matR{k});
end
-
- %opt_out_struct.(curPol)=opt_in_struct.(curPol).envelope;
end
+end
+
- beta_const = state.beta.('X')(1);
- beta_1 = state.beta.('X')(2);
- beta_2 = state.beta.('X')(3);
- beta_3 = state.beta.('X')(4);
- deltaomega = state.omega;
- beta_x = beta_const + beta_1 * deltaomega + 1/2 * beta_2 * deltaomega.^2 + 1/6 *beta_3 * deltaomega.^3;
-
-% opt_in_x = gpuArray(opt_in_x);
-% opt_in_y = gpuArray(opt_in_y);
-
-
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- % Split Step Method
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Split Step Method
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% [opt_out_x,opt_out_y] = split_step_loop(state.L,opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin,...
% state.lin_z_test,state.corr_length,state.n_plates_done,state.missing_dz,state.brf,state.common_beta,....
@@ -44,35 +75,33 @@ function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
% [opt_out_x,opt_out_y] = split_step_loop_mex(state.L,opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin,...
% state.lin_z_test,state.corr_length,state.n_plates_done,state.missing_dz,state.brf,state.common_beta,....
% state.chi,state.manakov,state.beat_len);
-
- % get nonlinear step size
- [state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
- %[state.dz] = getNLstepsize_original(state,opt_out_struct);
-
- state.n_step = 0;
- state.z_prop = 0;
- state.test_dz = [];
- state.powers = [];
- tic
+% get nonlinear step size
+[state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
+%[state.dz] = getNLstepsize_original(state,opt_out_struct);
- while state.z_prop < state.L
+state.n_step = 0;
+state.z_prop = 0;
+state.test_dz = [];
+state.powers = [];
- % reduce step length (dz) if we are to overshoot the fiber length
- % (L) in the next step
- if state.z_prop + state.dz > state.L
- state.dz = state.L - state.z_prop;
- end
-
- % update step number (n)
- state.n_step=state.n_step+1;
+while state.z_prop < state.L
- % append current step length to logbook (dzs)
- state.dzs(state.n_step)=state.dz;
+ % reduce step length (dz) if we are to overshoot the fiber length
+ % (L) in the next step
+ if state.z_prop + state.dz > state.L
+ state.dz = state.L - state.z_prop;
+ end
+
+ % update step number (n)
+ state.n_step=state.n_step+1;
+
+ % append current step length to logbook (dzs)
+ state.dzs(state.n_step)=state.dz;
-
- % half linear step
- [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
+
+ % half linear step
+ [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,...
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X]...
@@ -82,12 +111,12 @@ function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X);
- % complete nonlinear step
-
- [opt_in_x,opt_in_y] = nl_step(opt_in_x,opt_in_y, state.dz, state.gamma, state.chi, state.manakov, state.beat_len ,state.alpha_lin.X, state.alpha_lin.Y);
-
- % half linear step
- [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
+ % complete nonlinear step
+
+ [opt_in_x,opt_in_y] = nl_step(opt_in_x,opt_in_y, state.dz, state.gamma, state.chi, state.manakov, state.beat_len ,state.alpha_lin.X, state.alpha_lin.Y);
+
+ % half linear step
+ [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,...
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X]...
@@ -97,24 +126,34 @@ function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X);
- % get nonlinear step size
- [state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
- %[state.dz] = getNLstepsize_original(state,opt_out_struct);
+ % get nonlinear step size
+ [state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
+ %[state.dz] = getNLstepsize_original(state,opt_out_struct);
-
- end
-
- toc
-
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% GPU Gather (if enabled)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+if useGPU
+ opt_out_x = gather(opt_in_x);
+ opt_out_y = gather(opt_in_y);
- opt_out_x = (opt_in_x);
- opt_out_y = (opt_in_y);
+ % Gather state arrays back to CPU
+ state.common_beta.X = gather(state.common_beta.X);
+ state.common_beta.Y = gather(state.common_beta.Y);
+ state.brf.db1 = gather(state.brf.db1);
+ state.brf.db0 = gather(state.brf.db0);
+ for k = 1:numel(state.brf.matR)
+ state.brf.matR{k} = gather(state.brf.matR{k});
+ end
+else
+ opt_out_x = opt_in_x;
+ opt_out_y = opt_in_y;
+end
-% opt_out_x = gather(opt_in_x);
-% opt_out_y = gather(opt_in_y);
-
end
\ No newline at end of file
diff --git a/Classes/02_optical/dp_fiber_lib/canUseGPU.m b/Classes/02_optical/dp_fiber_lib/canUseGPU.m
new file mode 100644
index 0000000..84666bb
--- /dev/null
+++ b/Classes/02_optical/dp_fiber_lib/canUseGPU.m
@@ -0,0 +1,20 @@
+function canUse = canUseGPU()
+%CANUSEGPU Check if a compatible GPU is available for parallel computing
+% Returns true if MATLAB Parallel Computing Toolbox is available and
+% a CUDA-capable GPU is detected.
+
+canUse = false;
+
+% Check if Parallel Computing Toolbox is installed
+if ~license('test', 'Distrib_Computing_Toolbox')
+ return;
+end
+
+% Check for GPU device
+try
+ gpu = gpuDevice();
+ canUse = gpu.DeviceSupported;
+catch
+ canUse = false;
+end
+end
diff --git a/Classes/02_optical/dp_fiber_lib/getNLstepsize.m b/Classes/02_optical/dp_fiber_lib/getNLstepsize.m
index a70edb9..fd42eba 100644
--- a/Classes/02_optical/dp_fiber_lib/getNLstepsize.m
+++ b/Classes/02_optical/dp_fiber_lib/getNLstepsize.m
@@ -1,25 +1,25 @@
function [rDZ] = getNLstepsize(ux,uy,gamma,dzmin,dzmax,dphimax,alpha_lin)
-
- maxPow = max(gamma.*max(real(ux).^2+imag(ux).^2+real(uy).^2+imag(uy).^2));
-
- Leff = dphimax/maxPow;
- alpha_lin = max([alpha_lin.X alpha_lin.Y]);
- nl_att_len_ratio = alpha_lin*Leff;
-
- if nl_att_len_ratio >= 1
- rDZ = dzmax;
+% gather() handles gpuArray inputs - ensures scalar is on CPU for comparisons
+maxPow = gather(max(gamma.*max(real(ux).^2+imag(ux).^2+real(uy).^2+imag(uy).^2)));
+
+Leff = dphimax/maxPow;
+alpha_lin = max([alpha_lin.X alpha_lin.Y]);
+nl_att_len_ratio = alpha_lin*Leff;
+
+if nl_att_len_ratio >= 1
+ rDZ = dzmax;
+else
+ if alpha_lin == 0
+ step = Leff;
else
- if alpha_lin == 0
- step = Leff;
- else
- %effective length?
- step = -1/alpha_lin*log(1-nl_att_len_ratio);
- end
-
- rDZ = min([step dzmax]);
- rDZ = max([rDZ dzmin]);
- end
-
+ %effective length?
+ step = -1/alpha_lin*log(1-nl_att_len_ratio);
+ end
+
+ rDZ = min([step dzmax]);
+ rDZ = max([rDZ dzmin]);
+end
+
end
\ No newline at end of file
diff --git a/Classes/02_optical/dp_fiber_lib/lin_step.m b/Classes/02_optical/dp_fiber_lib/lin_step.m
index eea8a1e..cdb3562 100644
--- a/Classes/02_optical/dp_fiber_lib/lin_step.m
+++ b/Classes/02_optical/dp_fiber_lib/lin_step.m
@@ -22,37 +22,37 @@ n_plates_left = n_plates - n_plates_done;
% compute last plate size ( if it fits, it should be 0)
if missing_dz > aStepSize
-
+
last_plate = aStepSize;
missing_dz = missing_dz-aStepSize;
plate_sizes = last_plate;
plate_numbers = n_plates;
-
+
else
-
+
last_plate = aStepSize - missing_dz - (n_plates_left-1)*corr_length;
-
+
if missing_dz == 0
missing_dz = [];
end
-
+
%build vector of plate lengths with missing plate part from prev.
%iterartion , then some normal plates and finally a fraction of a plate
%to fit into the step length
plate_sizes = [missing_dz corr_length*ones(1,n_plates_left-1) last_plate];
-
+
if n_plates_done == 0
plate_numbers =[(n_plates_done+1):(n_plates-1) n_plates];
else
plate_numbers = [n_plates_done (n_plates_done+1):(n_plates-1) n_plates]; % not wrking yet
end
-
- %remember for next step
+
+ %remember for next step
missing_dz = corr_length - last_plate;
-
+
end
-plate_steps = repmat(n_step,1,length(plate_sizes));
+plate_steps = repmat(n_step,1,length(plate_sizes)); %#ok<NASGU>
%
%figure;stem(plate_sizes);
@@ -66,50 +66,39 @@ test_plate_numbers = [test_plate_numbers, plate_numbers];
opt_x=fft(opt_x);
opt_y=fft(opt_y);
-% db1 = gpuArray(brf.db1);
-% db0 = gpuArray(brf.db0);
-% common_beta_x = gpuArray(common_beta_x);
-% common_beta_y = gpuArray(common_beta_y);
-
-db1 = (brf.db1);
-db0 = (brf.db0);
-common_beta_x = (common_beta_x);
-common_beta_y = (common_beta_y);
+% Note: db1, db0, common_beta_x, common_beta_y are already on GPU when
+% useGPU=true (transferred in CNLSE_plain)
+db1 = brf.db1;
+db0 = brf.db0;
% process every waveplate with given sizes in plate_sizes
for n=1:length(plate_sizes)
dz = plate_sizes(n);
-
- % figure(87);subplot(2,1,1);plot(real(x(900:1150)));subplot(2,1,2);plot(real(y(900:1150)));
- % MOV1=[MOV1 getframe(87)];
-
+
% extract rotation matrix from pre calculated matrices
matR = brf.matR{plate_numbers(n)};
-
+
% transform to eigenvalue of of fiber segment
tOpt.X = conj(matR(1,1))*opt_x + conj(matR(2,1))*opt_y;
tOpt.Y = conj(matR(1,2))*opt_x + conj(matR(2,2))*opt_y;
-
+
% calculate statistical delta beta for pmd
delta_beta = 0.5*(db1+db0(n))/corr_length;
- % build transfer function with delta beta
- %common.beta = beta1+beta2*omega^2
-
+
%accumulate delta beta for log...
brf.simdgd = brf.simdgd + (db1(length(db1)/2+1)+db0(n))/corr_length;
-
+
h.X = exp(-1j*(common_beta_x-delta_beta)*dz);
h.Y = exp(-1j*(common_beta_y+delta_beta)*dz);
- % delta_beta has to be added to the transfer function
-
+
% process with transfer function
tOpt.X = h.X.*tOpt.X ;
tOpt.Y = h.Y.*tOpt.Y ;
-
+
% rotate back
opt_x = matR(1,1)*tOpt.X + matR(1,2)*tOpt.Y;
opt_y = matR(2,1)*tOpt.X + matR(2,2)*tOpt.Y;
-
+
end
lin_z_test = lin_z_test + sum(plate_sizes,2);
@@ -117,9 +106,8 @@ lin_z_test = lin_z_test + sum(plate_sizes,2);
%update the number of processed plates so far
n_plates_done = n_plates_done + n_plates_left;
-% attanuate the signal each linear state with alpha
-% ( 0.2dB = 4.6052e-05 )
-rOpt_x=ifft(exp(-alpha_lin_x*aStepSize/2).*opt_x); % /2 not sure why (have to find it in formulas)
-rOpt_y=ifft(exp(-alpha_lin_y*aStepSize/2).*opt_y); % but not relevant for now
+% attenuate the signal each linear step with alpha
+rOpt_x=ifft(exp(-alpha_lin_x*aStepSize/2).*opt_x);
+rOpt_y=ifft(exp(-alpha_lin_y*aStepSize/2).*opt_y);
-end
\ No newline at end of file
+end
diff --git a/Classes/04_DSP/Equalizer/EQ.m b/Classes/04_DSP/Equalizer/EQ.m
index f9d58d2..a03d66b 100644
--- a/Classes/04_DSP/Equalizer/EQ.m
+++ b/Classes/04_DSP/Equalizer/EQ.m
@@ -10,10 +10,6 @@ classdef EQ < handle
training_length %Number of training symbols
training_loops %Number of loops through sequence for training mode
ideal_dfe %Error free DFE decisions
- weighted_DFE %Weighted DFE on/off
- weighted_DFE_mode %Weighted DFE mode
- weighted_DFE_d_min %d_min threshold parameter for weighted DFE mode 1
- weighted_DFE_I_mode %[a_s, b_s, I_max]-parameters for the weighted DFE
DB_aim %Aim at duobinary output sequence
@@ -72,10 +68,6 @@ classdef EQ < handle
options.training_length = 1024 %Number of training symbols
options.training_loops = 1 %Number of loops through sequence for training mode
options.ideal_dfe = 0 %Error free DFE decisions
- options.weighted_DFE = 0;
- options.weighted_DFE_mode = 'R1';
- options.weighted_DFE_d_min = 0.5;
- options.weighted_DFE_I_mode = [5,0.5,0.6];
options.DB_aim %Aim at duobinary output sequence
@@ -446,43 +438,6 @@ classdef EQ < handle
dd_out(k) = constellation_in_(dd_idx);
end
- % Implementation of a weighted DFE in
- % order to prevent error propagation.
- % For further details, study [1], chapter 3.2.2 -
- % Modifications of DFE
-
- if obj.weighted_DFE
- % define new constellations
- const = unique(ref_in);
-
- % determine reliability factor gamma_k
- if output_vec(m) > min(const) && output_vec(m) < max(const)
- gamma_k = 1 - abs(output_vec(m) - dd_out(k));
- else
- gamma_k = 1;
- end
-
- % select mode
- if strcmp(obj.weighted_DFE_mode,'R1')
- if gamma_k >= obj.weighted_DFE_d_min
- f_gamma_k = 1;
- else
- f_gamma_k = 0;
- end
- elseif strcmp(obj.weighted_DFE_mode,'R2')
- f_gamma_k = gamma_k;
- elseif strcmp(obj.weighted_DFE_mode,'I1')
- nom = 1-exp(-obj.weighted_DFE_I_mode(1)*((gamma_k/obj.weighted_DFE_I_mode(2))-1));
- denom = 1+exp(-obj.weighted_DFE_I_mode(1)*((gamma_k/obj.weighted_DFE_I_mode(2))-1));
- f_gamma_k = (1/2)*((nom/denom) - 1);
- elseif strcmp(obj.weighted_DFE_mode,'I2')
- nom = 1-exp(-obj.weighted_DFE_I_mode(1)*((gamma_k/obj.weighted_DFE_I_mode(2))-1));
- denom = 1+exp(-obj.weighted_DFE_I_mode(1)*((gamma_k/obj.weighted_DFE_I_mode(2))-1));
- f_gamma_k = (obj.weighted_DFE_I_mode(3)/2)*((nom/denom) - 1);
- end
- % calculate weighted output
- output_vec(m) = f_gamma_k.*dd_out(k)+(1-f_gamma_k).*output_vec(m);
- end
if obj.Nb(1) > 0
dd_DFE(2:end) = dd_DFE(1:end-1);
@@ -784,6 +739,3 @@ classdef EQ < handle
end
end
-% References
-% [1] T. J. Wettlin, “Experimental Evaluation of Advanced Digital Signal Processing for Intra-Datacenter Systems using Direct-Detection,” 2023. [Online]. Available: https://nbn-resolving.org/urn:nbn:de:gbv:8:3-2023-00703-8
-
diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m
index 9965525..168b837 100644
--- a/Classes/04_DSP/Equalizer/ML_MLSE.m
+++ b/Classes/04_DSP/Equalizer/ML_MLSE.m
@@ -597,7 +597,7 @@ classdef ML_MLSE < handle
obj.S = obj.nSym;
obj.Nf = obj.order * obj.sps;
obj.nStates = obj.S^obj.L;
- obj.nFeasible = obj.nStates * obj.S;
+ obj.nFeasible = obj.nStates * obj.S; %feasible state transitions
% --- Trellis mapping
obj.trellis_states = reshape(obj.constellation,1,[]);
diff --git a/Classes/04_DSP/Timing Recovery/Godard_Timing_Recovery.m b/Classes/04_DSP/Timing Recovery/Godard_Timing_Recovery.m
new file mode 100644
index 0000000..12eeeaf
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Godard_Timing_Recovery.m
@@ -0,0 +1,112 @@
+classdef Godard_Timing_Recovery < handle
+
+ properties(Access=public)
+ mode
+ num_blocks
+ fft_length
+ sps
+ rolloff
+ mu
+ Ki
+ end
+
+ methods(Access=public)
+ function obj = Godard_Timing_Recovery(options)
+ arguments(Input)
+
+ options.mode = 0;
+ options.num_blocks = 1;
+ options.fft_length = 1024;
+ options.sps = 2;
+ options.rolloff = 0;
+ options.mu = 0;
+ options.Ki = 1e-3;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out, tau_hat] = process(obj, data_in)
+
+ data_out = data_in;
+ output_vector = zeros(length(data_in),1);
+ block_length = length(data_in)/obj.num_blocks;
+ for i = 1:obj.num_blocks
+ i_start = ((i-1)*block_length)+1;
+ i_end = i*block_length;
+ x = data_in.signal(i_start:i_end);
+ beta = obj.rolloff;
+ eta = obj.sps;
+ R = fft(x,obj.fft_length);
+ R_full = fft(x);
+
+ if obj.mode == 0 % Classic Godard
+ % Calculate time shift
+ k0 = (1:obj.fft_length/2).';
+ idx1 = k0;
+ idx2 = k0+(obj.fft_length/2);
+ tau_hat = sum(imag(R(idx1) .* conj(R(idx2))));
+ elseif obj.mode == 1 || obj.mode == 2 % Modified Godard 1
+ % Calculate Shift for the received signal
+ shiftBins = (1 - 1/eta) * obj.fft_length;
+ if abs(shiftBins - round(shiftBins)) > 1e-12
+ disp('Warning: shiftBins=(1-1/eta)*fft_length is non-integer. Choose compatible values for fft_length and eta.');
+ end
+ shiftBins = round(shiftBins);
+
+ % Calculate upper and lower bounds
+ kStart = ((1-beta)/(2*eta)) * obj.fft_length;
+ kEnd = ((1+beta)/(2*eta)) * obj.fft_length - 1;
+ if abs(kStart - round(kStart)) > 1e-12 || abs(kEnd - round(kEnd)) > 1e-12
+ disp('Warning: kStart/kEnd are non-integer. Choose compatible values for fft_length, eta, and beta.');
+ end
+ kStart = round(kStart);
+ kEnd = round(kEnd);
+
+ k0 = (kStart:kEnd).';
+ idx1 = k0 + 1;
+ idx2 = mod(k0 + shiftBins, obj.fft_length) + 1;
+
+ % Calculate time shift
+ if obj.mode == 1
+ tau_hat = sum(imag(R(idx1) .* conj(R(idx2))));
+ elseif obj.mode == 2
+ tau_hat = sum(angle(R(idx1))-angle(R(idx2)));
+ end
+ end
+
+
+ % % Normalization
+ % denom = floor(log10(abs(tau_hat)));
+ % tau_hat = tau_hat / 10^denom;
+
+ % Calculate mu using a first-order loop filter
+ mu_block = obj.mu + obj.Ki * tau_hat;
+
+ % % Shifting the signal in time domain using interpolation
+ % x_original = linspace(0,length(x)-1,length(x)).';
+ % x_new = x_original + mu_block;
+ % output_vector(i_start:i_end) = interp1(x_original,x,x_new,'linear','extrap');
+
+ % Shifting the signal in frequency domain
+ k = (0:block_length-1).';
+ phaseRamp = exp(-1j * 2*pi * (k/block_length) * mu_block);
+ R_shifted = R_full .* phaseRamp;
+
+ output_vector(i_start:i_end) = real(ifft(R_shifted));
+
+ end
+ data_out.signal = output_vector;
+ end
+
+
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/MaxVar_Timing_Recovery.m b/Classes/04_DSP/Timing Recovery/MaxVar_Timing_Recovery.m
new file mode 100644
index 0000000..d6517d5
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/MaxVar_Timing_Recovery.m
@@ -0,0 +1,111 @@
+classdef MaxVar_Timing_Recovery < handle
+
+ properties(Access=public)
+ mode
+ sps
+ fsym
+ fadc
+ num_tau
+ comp_signal
+ comp_mode
+ end
+
+ methods(Access=public)
+ function obj = MaxVar_Timing_Recovery(options)
+ arguments(Input)
+
+ options.mode = 0;
+ options.sps = 2;
+ options.fsym = 32e9;
+ options.fadc = 80e9;
+ options.num_tau = 64;
+ options.comp_signal = 0;
+ options.comp_mode = 0;
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out] = process(obj, data_in)
+
+ data_out = data_in;
+ x = data_in.signal;
+
+ if obj.comp_mode
+ our_signal = data_in;
+ their_signal = obj.comp_signal;
+ end
+
+ if obj.mode == 0
+ vars = zeros(obj.sps,1);
+ for phi = 1:obj.sps
+
+ % Test variance for different start samples
+ y_phi = x(phi:obj.sps:end);
+ vars(phi) = var(y_phi);
+
+ % Comparison to reference signal
+ if obj.comp_mode
+ our_signal.signal = x(phi:obj.sps:end);
+ our_signal.fs = 6e9;
+ our_signal.normalize("mode","rms").plot("displayname",['Our signal, ' num2str(vars(phi))],'fignum',1231+phi);
+ their_signal.normalize("mode","rms").plot("displayname",'Their signal','fignum',1231+phi);
+ end
+
+ end
+
+ % Choose signal configuration with the maximum variance
+ [~,phi_opt] = max(vars);
+
+ y = x(phi_opt:obj.sps:end).';
+
+ elseif obj.mode == 1
+ % Create a grid with different (sub)sample starting points
+ T = 1/obj.fsym;
+ t = (0:length(x)-1)/obj.fadc;
+ tauGrid = linspace(0, T, obj.num_tau);
+
+ % Interpolate the signal starting from every defined point
+ % and calculate the MMSE/variance
+ vars = zeros(size(tauGrid));
+ for i = 1:numel(tauGrid)
+ tau = tauGrid(i);
+ % tk = linspace(tau, t(end), length(x)/obj.sps);
+ tk = tau : T : t(end)+tau;
+ xk = interp1(t, x, tk, 'linear', 'extrap');
+ if obj.comp_mode % MMSE
+ e = xk.' - obj.comp_signal.signal;
+ vars(i) = mean(abs(e).^2);
+ else % Variance
+ vars(i) = var(xk, 1);
+ end
+ end
+
+ if obj.comp_mode % MMSE
+ [~,idx] = min(vars);
+ else % Variance
+ [~,idx] = max(vars);
+ end
+ tau_opt = tauGrid(idx);
+
+ % Choosing the signal at optimum
+ % tk = linspace(tau_opt, t(end), length(x)/obj.sps);
+ tk = tau_opt : T : t(end)+tau_opt;
+ y = interp1(t, x, tk, 'linear', 'extrap');
+ end
+ % if length(y) ~= length(x)/obj.sps
+ % y = [y 0];
+ % end
+ data_out.signal = y.';
+ end
+
+
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/Time_Shifter.m b/Classes/04_DSP/Timing Recovery/Time_Shifter.m
new file mode 100644
index 0000000..f08e3fe
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Time_Shifter.m
@@ -0,0 +1,39 @@
+classdef Time_Shifter < handle
+
+ properties(Access=public)
+ value
+ end
+
+ methods(Access=public)
+ function obj = Time_Shifter(options)
+ arguments(Input)
+
+ options.value = 0;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out, tau_hat] = process(obj, data_in)
+
+ data_out = data_in;
+ x = data_in.signal;
+ R = fft(x);
+ k = (0:length(R)-1).';
+ phaseRamp = exp(-1j * 2*pi * (k/length(R)) * obj.value);
+ R_shifted = R .* phaseRamp;
+ data_out.signal = real(ifft(R_shifted));
+
+ end
+
+
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/Timing_Recovery.m b/Classes/04_DSP/Timing Recovery/Timing_Recovery.m
new file mode 100644
index 0000000..3999ad7
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Timing_Recovery.m
@@ -0,0 +1,49 @@
+classdef Timing_Recovery < handle
+
+ properties(Access=public)
+ modulation
+ timing_error_detector
+ sps
+ damping_factor
+ normalized_loop_bandwidth
+ detector_gain
+ end
+
+ methods(Access=public)
+ function obj = Timing_Recovery(options)
+ arguments(Input)
+
+ options.modulation = 'PAM/PSK/QAM';
+ options.timing_error_detector = 'Gardner (non-data-aided)';
+ options.sps = 2;
+ options.damping_factor = 1.0;
+ options.normalized_loop_bandwidth = 0.01;
+ options.detector_gain = 2.7;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out, timing_error] = process(obj, data_in)
+
+ timing_synchronization = comm.SymbolSynchronizer( ...
+ "Modulation", obj.modulation,...
+ "TimingErrorDetector", obj.timing_error_detector, ...
+ "SamplesPerSymbol", obj.sps, ...
+ "DampingFactor", obj.damping_factor, ...
+ "NormalizedLoopBandwidth", obj.normalized_loop_bandwidth, ...
+ "DetectorGain", obj.detector_gain);
+ data_out = data_in;
+ [data_out.signal, timing_error] = timing_synchronization(data_in.signal);
+
+ end
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/Timing_Recovery_GPT.m b/Classes/04_DSP/Timing Recovery/Timing_Recovery_GPT.m
new file mode 100644
index 0000000..33d0509
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Timing_Recovery_GPT.m
@@ -0,0 +1,77 @@
+classdef Timing_Recovery_GPT < handle
+
+ properties(Access=public)
+ sps
+ muGrid
+ end
+
+ methods(Access=public)
+ function obj = Timing_Recovery_GPT(options)
+ arguments(Input)
+
+ options.sps = 2;
+ options.muGrid = 0;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function [data_out, mu_best, score] = process(obj, data_in)
+ %MAXVARTIMINGSYNC Choose sampling phase mu that maximizes variance of downsampled symbols.
+ %
+ % data_in : matched-filtered samples (complex or real), length N
+ % sps : samples per symbol (here typically 2)
+ % muGrid : candidate fractional offsets in [0,1)
+ %
+ % data_out : symbol-rate samples (length floor(N/sps))
+ % mu_best: chosen fractional offset
+ % score : variance score for each mu in muGrid
+
+ data_out = data_in;
+
+ x = data_in.signal(:);
+ N = length(x);
+ Ns = floor(N/obj.sps);
+
+ if nargin < 3 || isempty(obj.muGrid)
+ obj.muGrid = linspace(0, 0.99, 101); % 0..0.99 in ~0.01 steps
+ end
+
+ % Symbol indices (1-based sample positions)
+ n0 = 1; % start sample index
+ k = (0:Ns-1).';
+ tBase = n0 + k*obj.sps; % integer times (1, 1+sps, ...)
+
+ score = zeros(numel(obj.muGrid),1);
+
+ for m = 1:numel(obj.muGrid)
+ mu = obj.muGrid(m);
+ t = tBase + mu;
+
+ % Linear fractional sampling
+ y = interp1(1:N, x, t, 'linear', 'extrap');
+
+ % For PAM, maximize variance of real part (or abs if you prefer)
+ yr = real(y);
+ score(m) = var(yr, 1); % use population variance (normalization doesn't matter for argmax)
+ end
+
+ % Pick best mu
+ [~, idx] = max(score);
+ mu_best = obj.muGrid(idx);
+
+ % Resample with best mu
+ t = tBase + mu_best;
+ data_out.signal = interp1(1:N, x, t, 'linear', 'extrap');
+
+ end
+ end
+end
+
diff --git a/Classes/04_DSP/Timing Recovery/Timing_Recovery_Move_It.m b/Classes/04_DSP/Timing Recovery/Timing_Recovery_Move_It.m
new file mode 100644
index 0000000..bbd0578
--- /dev/null
+++ b/Classes/04_DSP/Timing Recovery/Timing_Recovery_Move_It.m
@@ -0,0 +1,77 @@
+classdef Timing_Recovery_Move_It < handle
+
+ properties(Access=public)
+ f_sim
+ gamma
+ end
+
+ methods(Access=public)
+ function obj = Timing_Recovery_Move_It(options)
+ arguments(Input)
+
+ options.f_sim = 14e9;
+ options.gamma = 0.1;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+ %obj-Initialization here%
+
+ end
+
+ function data_out = process(obj, data_in)
+
+ e = NaN(size(data_in.signal));
+ T = 1/obj.f_sim;
+ t = zeros(size(data_in.signal));
+ t(2) = T/2;
+ mu = zeros(size(data_in.signal));
+
+% n = 2;
+ for k = 3:2:length(data_in.signal)
+ data_in.signal(k-1) = data_in.signal(k-1)*(1-mu(k-2)) + data_in.signal(k)*mu(k-2);
+ data_in.signal(k) = data_in.signal(k)*(1-mu(k-1)) + data_in.signal(k+1)*mu(k-1);
+
+ % TED
+ e(k) = (data_in.signal(k-2)-data_in.signal(k))*data_in.signal(k-1);
+ e(k+1) = e(k);
+
+ % TED Mueller Mueller
+% e(k) = ref_in(n-1)*data_in.signal(k) - ref_in(n)*data_in.signal(k-2);
+% e(k+1) = e(k);
+% n = n+1;
+%
+ % interpolator control
+% t(k) = t(k-1) + T/2 + obj.gamma/2*e(k);
+% t(k+1) = t(k) + T/2 + obj.gamma/2*e(k+1);
+ t(k) = t(k-1) + T/2 + obj.gamma*e(k)*T/2;
+ t(k+1) = t(k) + T/2 + obj.gamma*e(k+1)*T/2;
+% t(k) = k*T/2 + obj.gamma*e(k)*T/2;
+% t(k+1) = k*T/2 + obj.gamma*e(k+1)*T/2;
+
+ % interpolator
+ mu(k) = t(k)/(T/2) - round(t(k)/(T/2));
+ mu(k+1) = t(k+1)/(T/2) - round(t(k+1)/(T/2));
+
+ thres = 0.7;
+ if mu(k)-mu(k-1) > thres
+ mu(k) = mu(k) - 1;
+ elseif mu(k) - mu(k-1) < -thres
+ mu(k) = mu(k) + 1;
+ end
+
+ if mu(k+1)-mu(k) > thres
+ mu(k+1) = mu(k+1) - 1;
+ elseif mu(k+1) - mu(k) < -thres
+ mu(k+1) = mu(k+1) + 1;
+ end
+ end
+ data_out = data_in;
+ end
+ end
+end
+
diff --git a/Classes/04_DSP/Timing_Recovery.m b/Classes/04_DSP/Timing_Recovery.m
new file mode 100644
index 0000000..23cc331
--- /dev/null
+++ b/Classes/04_DSP/Timing_Recovery.m
@@ -0,0 +1,47 @@
+classdef Timing_Recovery < handle
+
+ properties(Access=public)
+ timing_error_detector
+ sps
+ damping_factor
+ normalized_loop_bandwidth
+ detector_gain
+ end
+
+ methods(Access=public)
+ function obj = Timing_Recovery(options)
+ arguments(Input)
+
+ options.timing_error_detector = 'Gardner';
+ options.sps = 2;
+ options.damping_factor = 1.0;
+ options.normalized_loop_bandwidth = 0.005;
+ options.detector_gain = 1;
+
+ end
+
+ fn = fieldnames(options);
+ for n = 1:numel(fn)
+ obj.(fn{n}) = options.(fn{n});
+ end
+
+
+
+ end
+
+ function [data_out,timing_error] = process(obj, data_in)
+
+ timing_synchronization = comm.SymbolSynchronizer( ...
+ "TimingErrorDetector", obj.timing_error_detector, ...
+ "SamplesPerSymbol", obj.sps, ...
+ "DampingFactor", obj.damping_factor, ...
+ "NormalizedLoopBandwidth", obj.normalized_loop_bandwidth, ...
+ "DetectorGain", obj.detector_gain);
+
+ data_out = data_in;
+ [data_out.signal,timing_error] = timing_synchronization(data_in.signal);
+
+ end
+ end
+end
+
diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m
index 5569746..b7ba026 100644
--- a/Classes/DataBaseHandler/DBHandler.m
+++ b/Classes/DataBaseHandler/DBHandler.m
@@ -23,6 +23,12 @@ classdef DBHandler < handle
arguments
options.dataBase = ""; % Default value for pathToDB if not provided
options.type = "mysql";
+ options.server = "";
+ options.port = 3306;
+ options.user = "";
+ options.password = "";
+
+
end
% Assign values to class properties based on input arguments
@@ -46,12 +52,13 @@ classdef DBHandler < handle
obj.conn = database( ...
string(obj.dataBase), ... % Database name
- "silas", ... % Username
- "silas", ... % Password (or getSecret)
+ options.user, ... % Username
+ options.password, ... % Password (or getSecret)
"Vendor", "MySQL", ...
- "Server", "134.245.243.254", ...
+ "Server", options.server, ...
"PortNumber", 3306, ...
"JDBCDriverLocation", "C:\Users\Silas\Documents\mysql-connector-j-9.3.0\mysql-connector-j-9.3.0.jar");
+
end
catch e
diff --git a/Functions/EQ_structures/ml_mlse.m b/Functions/EQ_structures/ml_mlse.m
index 84ff5cb..04fe317 100644
--- a/Functions/EQ_structures/ml_mlse.m
+++ b/Functions/EQ_structures/ml_mlse.m
@@ -54,12 +54,6 @@ for k = 1:numel(fn)
end
json_str = jsonencode(eq_small);
-fn = fieldnames(eq_);
-for k = 1:numel(fn)
- if issparse(eq_.(fn{k}))
- eq_.(fn{k}) = full(eq_.(fn{k}));
- end
-end
ml_mlse_results.config.eq = jsonencode(eq_);
ml_mlse_results.config.equalizer_structure = int32(equalizer_structure.ml_mlse);
ml_mlse_results.config.comment = 'function: ML-based MLSE';
@@ -93,7 +87,6 @@ end
function [bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, precode_mode, M, eth_style)
% Calculate BER based on precoding mode
mapper = PAMmapper(M, 0, "eth_style", eth_style);
-skip_front = 150;
switch precode_mode
case db_mode.no_db
@@ -108,11 +101,11 @@ switch precode_mode
tx_bits_precoded = mapper.demap(tx_symbols_precoded);
rx_bits = mapper.demap(eq_signal_hd_precoded);
- [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", skip_front, "skip_end", 150, "returnErrorLocation", 1);
+ [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
% B) Just determine BER
rx_bits = mapper.demap(eq_signal_hd);
- [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", skip_front, "skip_end", 150, "returnErrorLocation", 1);
+ [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
case db_mode.db_precoded
% Data is precoded on TX side
@@ -120,12 +113,12 @@ switch precode_mode
eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd, "M", M);
eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded, "M", M);
rx_bits_decoded = mapper.demap(eq_signal_hd_decoded);
- [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", skip_front, "skip_end", 150, "returnErrorLocation", 1);
+ [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
% B) Omit the Coding by comparing with demapped TX symbol sequence
tx_bits_demapped = mapper.demap(tx_symbols);
rx_bits = mapper.demap(eq_signal_hd);
- [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", skip_front, "skip_end", 150, "returnErrorLocation", 1);
+ [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
end
end
diff --git a/Functions/Theory/Dissertation/mach_zehnder_modulator.m b/Functions/Theory/Dissertation/mach_zehnder_modulator.m
new file mode 100644
index 0000000..f9eca58
--- /dev/null
+++ b/Functions/Theory/Dissertation/mach_zehnder_modulator.m
@@ -0,0 +1,219 @@
+
+
+% Parameters
+c0 = physconst('lightspeed'); % [m/s]
+lambda0 = 1310e-9; % [m]
+omega0 = 2*pi*c0/lambda0;
+
+L = 5e-3; % [m] effective phase section length (set as needed)
+n_eff = 2.2; % [-] effective index (set as needed)
+
+E0 = 1; % field amplitude (arbitrary)
+Vpi = 3.2; % [V] half-wave voltage (your V_pi)
+
+% Drive
+f0 = 1e9; % [Hz]
+fs = 200e9; % [Hz]
+Nper = 2; % number of periods
+Vpp = 0.6*Vpi; % [V] peak-to-peak of v_drive(t)
+
+biasV = 1.1; % [V] differential bias added to v_drive
+
+% Time axis + differential drive voltage v_drive(t)
+T = Nper/f0;
+t = (0:1/fs:T-1/fs).';
+
+
+if 1
+ % SINE
+ v_drive = biasV + (Vpp/2)*sin(2*pi*f0*t); % v_drive(t) (peak = Vpp/2)
+
+else
+
+ % --- Generate PAM-4 Sequence ---
+ symbols = linspace(-0.5, 0.5, 4);
+ num_symbols = 12; % Increased slightly for better visual
+ rng(44);
+ random_data = symbols(randi(4, 1, num_symbols));
+
+ % Create time axis (Note: T is your period from the sine code)
+ sps = round(T * fs);
+ t = (0:1/fs:(num_symbols*T)-1/fs).';
+
+ % Upsample to rectangular waveform
+ v_pam = repelem(random_data, sps).';
+
+ % Apply swing and bias: Resulting range is [biasV-Vpp/2, biasV+Vpp/2]
+ v_drive_rect = biasV + (v_pam * Vpp);
+
+ % --- Round the edges ---
+ filter_span = round(sps/1.5); % Increased span for smoother "rounding"
+ window = gausswin(filter_span);
+ window = window / sum(window);
+
+ % Apply filter (using 'same' to keep vector length, but be aware of edge transients)
+ v_drive = conv(v_drive_rect, window, 'same');
+
+end
+
+
+% Analytic
+v_ = linspace(-1,2, 2001);
+% Field transfer function (amplitude)
+Field_mzm_analytic = cos((pi/2)*v_);
+
+% Power transfer function (intensity)
+P_mzm_analytic = Field_mzm_analytic.^2;
+
+% Imbalance factor in YOUR notation:
+rho = 1;
+
+% Push-pull branch voltages (consistent with v_drive = v1 - v2)
+v1 = +0.5*v_drive; % arm 1
+v2 = -0.5*v_drive; % arm 2
+
+% Phases phi1, phi2
+phi1 = pi * v1 / Vpi;
+phi2 = pi * v2 / Vpi;
+
+% Fields: E_in and E_out (exactly your Eq. (mzm_e_field))
+E_in = E0 .* exp(1i*omega0*t);
+
+common_phase = exp(-1i * (omega0*L*n_eff/c0)); % exp(-j*omega0*L*n_eff/c0)
+
+E_out = E0 .* exp(1i*omega0*t) .* common_phase .* 0.5 .* ...
+ ( exp(-1i*phi1) + rho .* exp(-1i*phi2) );
+
+% Transfer function (numerical): E_out/E_in
+H_num = E_out ./ E_in;
+
+% Power (normalized)
+Pnorm_num = abs(H_num).^2; % since |E_out/E_in|^2
+
+% Ideal TF (analytic) for comparison (rho=1, push-pull)
+H_ideal = common_phase .* cos( (pi/2) * (v_drive./Vpi) );
+
+Pnorm_ideal = abs(H_ideal).^2;
+Pnorm_math = cos( (pi/2) * (v_drive./Vpi) ).^2;
+
+
+set(groot, 'defaultLegendInterpreter', 'tex');
+set(groot, 'defaultAxesTickLabelInterpreter', 'tex');
+set(groot, 'defaultTextInterpreter', 'tex');
+
+% Normalized voltage axis (multiples of Vpi)
+v_norm = v_drive./Vpi;
+
+colfield = [0,0,0]; %is black
+colpow = linspecer(2);
+colpow = colpow(1,:);
+colvdrive = linspecer(2);
+colvdrive = colvdrive(2,:);
+
+%% SIGNAL IN
+figure(1); clf
+plot(v_norm,t*1e9, 'LineWidth', 1.0,'Color',colvdrive); grid on;
+ylabel('t [ns]'); xlabel('v_{drive}(t)/V_\pi');
+title('Drive voltage (normalized)');
+xlim([min(v_) max(v_)]);
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\linear_casee\mzm_input_signal.tex');
+
+
+%% IN/OUT (static transfer) — normalized x-axis + analytic curve
+if 0
+figure(2); clf
+plot(v_, Field_mzm_analytic, 'LineWidth', 1.2,'LineStyle','--','Color',colfield); hold on;% analytic power TF
+plot(v_, P_mzm_analytic, 'LineWidth', 1.2, 'Color',colpow); hold on;% analytic power TF
+% show input time signal
+plot(v_norm,-1+t*1e9, 'LineWidth', 1.0,'Color',colvdrive); grid on;
+% show output time signal
+plot(2+t*1e9, Pnorm_num, 'LineWidth', 1.0,'DisplayName','Intensity', 'Color',colvdrive); hold on;
+plot(2+t*1e9, real(H_ideal), '--', 'LineWidth', 1.0,'DisplayName','Field','Color',colfield); hold on;
+scatter(v_norm, Pnorm_num, 12, '.', 'LineWidth', 1,'MarkerEdgeColor',colvdrive);
+scatter(biasV./Vpi,(cos((pi/2)*biasV./Vpi)^2),10,'Marker','o');
+line([min(v_drive), min(v_drive)]./Vpi,[(cos((pi/2)*min(v_drive)./Vpi)^2), -2],'linewidth',0.5,'color','black','linestyle','--');
+line([max(v_drive) max(v_drive)]./Vpi,[(cos((pi/2)*max(v_drive)./Vpi)^2), -2],'linewidth',0.5,'color','black','linestyle','--');
+xline([min(v_norm) max(v_norm)])
+
+grid on;
+xlabel('v_{drive}(t)/V_\pi'); ylabel('|E_{out}/E_{in}|^2');
+% legend
+xlim([min(v_) max(v_)+1]);
+ylim([-1 1]);
+
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\mzm.tex');
+end
+%%
+
+figure(3); clf
+plot(v_, Field_mzm_analytic, 'LineWidth', 1.2,'LineStyle','--','Color',colfield); hold on;% analytic power TF
+plot(v_, P_mzm_analytic, 'LineWidth', 1.2, 'Color',colpow); hold on;% analytic power TF
+
+scatter(v_norm, Pnorm_num, 12, '.', 'LineWidth', 1,'MarkerEdgeColor',colvdrive);
+scatter(biasV./Vpi,(cos((pi/2)*biasV./Vpi)^2),10,'Marker','o');
+line([min(v_drive), min(v_drive)]./Vpi,[(cos((pi/2)*min(v_drive)./Vpi)^2), -2],'linewidth',0.5,'color','black','linestyle','--');
+line([max(v_drive) max(v_drive)]./Vpi,[(cos((pi/2)*max(v_drive)./Vpi)^2), -2],'linewidth',0.5,'color','black','linestyle','--');
+xline([min(v_norm) max(v_norm)])
+
+grid on;
+xlabel('v_{drive}(t)/V_\pi'); ylabel('|E_{out}/E_{in}|^2');
+% legend
+xlim([min(v_) max(v_)]);
+ylim([-1 1]);
+
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\mzm_tramsfer_function_matlab.tex');
+
+
+%%
+
+figure(4); clf
+% plot(v_, Field_mzm_analytic, 'LineWidth', 1.2,'LineStyle','--','Color',colfield); hold on;% analytic power TF
+plot(v_, P_mzm_analytic, 'LineWidth', 1.2, 'Color','black'); hold on;% analytic power TF
+input_dots = linspace(min(v_drive),max(v_drive),4)./Vpi;
+% input_dots = unique(v_drive_rect)./Vpi;
+output_dots = (cos((pi/2)*input_dots).^2);
+scatter(input_dots,output_dots,'Marker','x','LineWidth',1,'MarkerEdgeColor','black');
+scatter(input_dots,zeros(size(input_dots)),'Marker','^','LineWidth',2,'MarkerEdgeColor','black');
+% scatter(ones(size(input_dots)),output_dots,'Marker','<','LineWidth',2,'MarkerEdgeColor','black');
+
+for i = 1:numel(input_dots)
+ % Draw the dashed projection lines
+ line([input_dots(i), input_dots(i)], [output_dots(i), 0], 'linewidth', 0.5, 'color', 'black', 'linestyle', '--', 'handlevisibility', 'off');
+ line([input_dots(i), 1], [output_dots(i), output_dots(i)], 'linewidth', 0.5, 'color', 'black', 'linestyle', '--', 'handlevisibility', 'off');
+
+ % Add the level annotation boxes near the output (y-axis)
+ % Adjust the '1.05' to move the box further right or 'output_dots(i)' for height
+ j = 3-(i-1)*2;
+ text(1, output_dots(i), sprintf('Level %d', j), ...
+ 'FontSize', 8, ...
+ 'EdgeColor', 'black', ...
+ 'BackgroundColor', 'white', ...
+ 'Margin', 2);
+end
+
+xlim([0,1.5]);
+ylim([0,1])
+
+% line([min(v_drive), min(v_drive)]./Vpi,[(cos((pi/2)*min(v_drive)./Vpi)^2), 0],'linewidth',0.5,'color','black','linestyle','--');
+% line([max(v_drive), max(v_drive)]./Vpi,[(cos((pi/2)*max(v_drive)./Vpi)^2), 0],'linewidth',0.5,'color','black','linestyle','--');
+
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\linear_casee\mzm_tf.tex');
+% xticks(sort(input_dots));
+% yticks(sort(output_dots));
+grid off
+
+
+%%
+% % FIELD TF (only field here; do not mix power into this figure)
+figure(5); clf
+% plot(t*1e9, real(H_num), 'LineWidth', 1.0); hold on;
+% plot(t*1e9, real(H_ideal), '--', 'LineWidth', 1.0,'DisplayName','Field','Color',colfield); hold on;
+plot(t*1e9, Pnorm_num, 'LineWidth', 1.0,'DisplayName','Intensity', 'Color',colpow); hold on;
+grid on;
+xlabel('t [ns]'); ylabel('Re\{E_{out}/E_{in}\}');
+legend
+yticks(sort(output_dots));
+% mat2tikz_improved('C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\linear_casee\mzm_output_signal.tex');
+
+
+
diff --git a/Functions/mat2tikz_improved.m b/Functions/mat2tikz_improved.m
new file mode 100644
index 0000000..a36b1d1
--- /dev/null
+++ b/Functions/mat2tikz_improved.m
@@ -0,0 +1,23 @@
+function mat2tikz_improved(filename)
+arguments
+ % Default to the path in your example if no argument is provided
+ filename (1,1) string = 'C:\Users\Silas\Documents\Dissertation\00_Examples\tikz\textfig.tikz';
+end
+cleanfigure;
+matlab2tikz(char(filename), ...
+ 'width','\fwidth', ...
+ 'height','\fheight', ...
+ 'showInfo',false, ...
+ 'extraAxisOptions',{ ...
+ 'legend style={font=\footnotesize}', ...
+ 'xlabel style={font=\color{white!15!black},font=\small},',...
+ 'ylabel style={font=\color{white!15!black},font=\small},',...
+ 'legend columns=1', ...
+ 'every axis/.append style={font=\scriptsize}',...
+ 'legend columns=1',...
+ 'legend style={at={(0.02,0.98)},font=\footnotesize,draw=black!60,rounded corners=2pt,inner sep=1pt,fill=white,column sep=6pt,anchor= north west}',...
+ 'legend style={at={(0.02,0.98)},draw=white!0!white,font=\scriptsize,inner sep=0.1pt,fill=white,column sep=1pt,anchor= north west}',...
+ 'every axis/.append style={font=\scriptsize}',...
+ });
+
+end
\ No newline at end of file
diff --git a/Libs/wesanderson_colors/WesPalette.m b/Libs/wesanderson_colors/WesPalette.m
index 7cd1dcb..13e976f 100644
--- a/Libs/wesanderson_colors/WesPalette.m
+++ b/Libs/wesanderson_colors/WesPalette.m
@@ -1,10 +1,13 @@
classdef WesPalette
% WESPALETTE Wes Anderson color palettes with auto-completion
% Usage:
- % cmap = WesPalette.Zissou1.rgb()
- % cmap = WesPalette.Zissou1.rgb(3)
-
- % https://github.com/karthik/wesanderson?tab=readme-ov-file
+ % cmap = WesPalette.Zissou1.rgb() % full palette
+ % cmap = WesPalette.Zissou1.rgb(3) % 3 colors (discrete default)
+ % cmap = WesPalette.Zissou1.rgb(12,"discrete") % any n, no interpolation
+ % cmap = WesPalette.Zissou1.rgb(256,"continuous") % smooth colormap (Lab interpolation)
+ %
+ % Requires:
+ % - colorspace.m (Pascal Getreuer) on MATLAB path for "continuous" mode
enumeration
BottleRocket1
@@ -34,21 +37,33 @@ classdef WesPalette
end
methods
- function cmap = rgb(obj, n)
+ function cmap = rgb(obj, n, mode)
% Return palette as Nx3 RGB colormap [0–1]
+ %
+ % n : number of requested colors (optional)
+ % mode : "discrete" (default) or "continuous"
- hex = obj.hex();
+ base_hex = obj.hex();
+ base_rgb = WesPalette.hex2rgb(base_hex);
- rgb = hex2rgb(hex);
+ if nargin < 2 || isempty(n)
+ cmap = base_rgb;
+ return;
+ end
+ if nargin < 3 || isempty(mode)
+ mode = "discrete";
+ end
+ mode = lower(string(mode));
- if nargin == 2
- if n > size(rgb,1)
- error('Requested %d colors, but only %d available.', ...
- n, size(rgb,1))
- end
- cmap = rgb(1:n,:);
+ validateattributes(n, {'numeric'}, {'scalar','integer','positive'}, mfilename, 'n');
+ if mode ~= "discrete" && mode ~= "continuous"
+ error('mode must be "discrete" or "continuous".');
+ end
+
+ if mode == "discrete"
+ cmap = WesPalette.sample_discrete(base_rgb, n);
else
- cmap = rgb;
+ cmap = WesPalette.interpolate_continuous_lab(base_rgb, n);
end
end
end
@@ -56,78 +71,116 @@ classdef WesPalette
methods (Access = private)
function hex = hex(obj)
% Internal HEX storage
-
switch obj
case WesPalette.BottleRocket1
hex = {'#A42820','#5F5647','#9B110E','#3F5151','#4E2A1E','#550307','#0C1707'};
-
case WesPalette.BottleRocket2
hex = {'#FAD510','#CB2314','#273046','#354823','#1E1E1E'};
-
case {WesPalette.Rushmore1, WesPalette.Rushmore}
hex = {'#E1BD6D','#EABE94','#0B775E','#35274A','#F2300F'};
-
case WesPalette.Royal1
hex = {'#899DA4','#C93312','#FAEFD1','#DC863B'};
-
case WesPalette.Royal2
hex = {'#9A8822','#F5CDB4','#F8AFA8','#FDDDA0','#74A089'};
-
case WesPalette.Zissou1
hex = {'#3B9AB2','#78B7C5','#EBCC2A','#E1AF00','#F21A00'};
-
case WesPalette.Zissou1Continuous
hex = {'#3A9AB2','#6FB2C1','#91BAB6','#A5C2A3','#BDC881', ...
'#DCCB4E','#E3B710','#E79805','#EC7A05','#EF5703','#F11B00'};
-
case WesPalette.Darjeeling1
hex = {'#FF0000','#00A08A','#F2AD00','#F98400','#5BBCD6'};
-
case WesPalette.Darjeeling2
hex = {'#ECCBAE','#046C9A','#D69C4E','#ABDDDE','#000000'};
-
case WesPalette.Chevalier1
hex = {'#446455','#FDD262','#D3DDDC','#C7B19C'};
-
case WesPalette.FantasticFox1
hex = {'#DD8D29','#E2D200','#46ACC8','#E58601','#B40F20'};
-
case WesPalette.Moonrise1
hex = {'#F3DF6C','#CEAB07','#D5D5D3','#24281A'};
-
case WesPalette.Moonrise2
hex = {'#798E87','#C27D38','#CCC591','#29211F'};
-
case WesPalette.Moonrise3
hex = {'#85D4E3','#F4B5BD','#9C964A','#CDC08C','#FAD77B'};
-
case WesPalette.Cavalcanti1
hex = {'#D8B70A','#02401B','#A2A475','#81A88D','#972D15'};
-
case WesPalette.GrandBudapest1
hex = {'#F1BB7B','#FD6467','#5B1A18','#D67236'};
-
case WesPalette.GrandBudapest2
hex = {'#E6A0C4','#C6CDF7','#D8A499','#7294D4'};
-
case WesPalette.IsleofDogs1
hex = {'#9986A5','#79402E','#CCBA72','#0F0D0E','#D9D0D3','#8D8680'};
-
case WesPalette.IsleofDogs2
hex = {'#EAD3BF','#AA9486','#B6854D','#39312F','#1C1718'};
-
case WesPalette.FrenchDispatch
hex = {'#90D4CC','#BD3027','#B0AFA2','#7FC0C6','#9D9C85'};
-
case WesPalette.AsteroidCity1
hex = {'#0A9F9D','#CEB175','#E54E21','#6C8645','#C18748'};
-
case WesPalette.AsteroidCity2
hex = {'#C52E19','#AC9765','#54D8B1','#B67C3B','#175149','#AF4E24'};
-
case WesPalette.AsteroidCity3
hex = {'#FBA72A','#D3D4D8','#CB7A5C','#5785C1'};
end
end
end
+
+ methods (Static, Access = private)
+ function rgb = hex2rgb(hex)
+ % hex: cellstr like {'#RRGGBB', ...}
+ if isstring(hex), hex = cellstr(hex); end
+ n = numel(hex);
+ rgb = zeros(n,3);
+ for i = 1:n
+ h = char(hex{i});
+ if startsWith(h,'#'), h = h(2:end); end
+ if numel(h) ~= 6
+ error('Invalid HEX color: %s', hex{i});
+ end
+ rgb(i,1) = hex2dec(h(1:2))/255;
+ rgb(i,2) = hex2dec(h(3:4))/255;
+ rgb(i,3) = hex2dec(h(5:6))/255;
+ end
+ end
+
+ function cmap = sample_discrete(base_rgb, n)
+ % No interpolation; allow any n by sampling/repeating.
+ k = size(base_rgb,1);
+
+ if n <= k
+ idx = round(linspace(1, k, n)); % spread across palette
+ idx = max(1, min(k, idx));
+ cmap = base_rgb(idx,:);
+ else
+ reps = floor(n / k);
+ rmd = mod(n, k);
+ cmap = [repmat(base_rgb, reps, 1); base_rgb(1:rmd,:)];
+ end
+ end
+
+ function cmap = interpolate_continuous_lab(base_rgb, n)
+ % Smooth interpolation in Lab using colorspace().
+ % Requires colorspace.m by Pascal Getreuer on MATLAB path.
+
+ k = size(base_rgb,1);
+ if k == 1
+ cmap = repmat(base_rgb, n, 1);
+ return;
+ end
+
+ % Convert to Lab, interpolate each channel, convert back
+ lab = colorspace('Lab<-RGB', base_rgb);
+
+ t_base = linspace(0, 1, k);
+ t_new = linspace(0, 1, n);
+
+ lab_new = zeros(n,3);
+ for c = 1:3
+ lab_new(:,c) = interp1(t_base, lab(:,c), t_new, 'linear');
+ end
+
+ rgb_new = colorspace('RGB<-Lab', lab_new);
+
+ % Clamp to displayable gamut
+ cmap = min(max(rgb_new, 0), 1);
+ end
+ end
end
diff --git a/Libs/wesanderson_colors/minimal_example_wespalette.m b/Libs/wesanderson_colors/minimal_example_wespalette.m
index 9fcd99b..f308f27 100644
--- a/Libs/wesanderson_colors/minimal_example_wespalette.m
+++ b/Libs/wesanderson_colors/minimal_example_wespalette.m
@@ -5,8 +5,8 @@ y2 = 1e0 ./ (1 + exp(-0.4*(x-12))); % NLPN
y3 = 1e-6 * 10.^(0.45*x); % RP on gamma
y4 = 1e-2 * 10.^(0.18*(x-8)); % RP on beta2
-cmap = WesPalette.AsteroidCity1.rgb(4);
-cmap = linspecer(4);
+cmap = WesPalette.AsteroidCity1;
+% cmap = linspecer(4);
figure1=figure(202998);clf;hold on
lw = 0.8; ms = 4;
plot(x,y1,'LineWidth',lw,'Color',cmap(1,:),'Marker','o','MarkerEdgeColor',cmap(1,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_FFE_DFE.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_FFE_DFE.m
new file mode 100644
index 0000000..29069ce
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_FFE_DFE.m
@@ -0,0 +1,36 @@
+taps_ffe = [200, 3, 2];
+taps_dfe = [5, 2, 1];
+trlen = 4096*2;
+trloops = 5;
+ddloops = 5;
+K_FFE = 1;
+DCmu = 0.005;
+mu_ffe_values = [0.001, 0.0005, 0.001];
+mu_dfe_values = 0.0007;
+DDmu = [mu_ffe_values, mu_dfe_values];
+DFEmu = 0.004;
+FFEmu = 0;
+plot_final = 0;
+idealdfe = 1;
+num_pf_coeffs = 1;
+damp_factor = 1;
+norm_loop_bw = 0.01;
+det_gain = 2.7;
+
+step_size = 0.0001;
+
+BER = [];
+taps_ffe_and_dfe = zeros(1,6);
+for i = 1:6
+ for j = 1:200
+ taps_ffe_and_dfe(i) = j;
+ BER_run = first_analysis_2(taps_ffe_and_dfe(1:3), taps_ffe_and_dfe(4:6), trlen, trloops, ddloops, ...
+ K_FFE, DCmu, DDmu, DFEmu, FFEmu, plot_final, idealdfe, num_pf_coeffs, ...
+ damp_factor, norm_loop_bw, det_gain);
+ BER = [BER, BER_run];
+ close all
+ end
+ [~,index_minimum_ber] = min(BER);
+ taps_ffe_and_dfe(i) = index_minimum_ber;
+end
+disp(taps_ffe_and_dfe)
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_ffe.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_ffe.m
new file mode 100644
index 0000000..8b55700
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_ffe.m
@@ -0,0 +1,57 @@
+%%
+ffe_start = 0;
+ffe_step = 5;
+ffe_end = 30;
+ffe_first_order = ffe_start:ffe_step:ffe_end;
+
+current = 225:10:285;
+current = string(current);
+power = [28.02, 33.2, 37.5, 42.3, 46.3, 49.3, 53.4];
+power = string(power);
+num_pf_coeff = 4;
+% taps_ffe = [300, 0, 0];
+taps_dfe = [0, 0, 0];
+M = 4;
+trlength = 4096*4;
+x = 225:10:285;
+BER_PAM_4 = [];
+
+for i = 1:length(ffe_first_order)
+ for eq_method = 2
+ for j = 4
+ BER_run = first_analysis_ber(current(j), power(j), num_pf_coeff, [300, 15, ffe_first_order(i)], taps_dfe, M, trlength, eq_method);
+ BER_PAM_4 = [BER_PAM_4, BER_run];
+ end
+ end
+end
+save('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_4_FFE_Second_Order_Tap_Sweep.mat', 'ffe_first_order', 'BER_PAM_4')
+BER_PAM_4 = [];
+
+%%
+BER = load('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_4_FFE_Second_Order_Tap_Sweep.mat');
+BER = BER.BER_PAM_4;
+
+figure(202120)
+plot(ffe_first_order, BER, '-o','LineWidth',1.75);
+hold on
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% FEC Labels direkt im Plot
+text(286,2.2e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(285,3.3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(282.5,5.4e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(287,2.4e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Number of DFE First Order Taps', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-4', 'Interpreter','latex')
+
+grid minor
+% ylim([1e-4 5e-1])
+set(gca,'YScale','log')
+% beautifyBERplot
+hold off
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_optimize_timing_recovery_parameters.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_optimize_timing_recovery_parameters.m
new file mode 100644
index 0000000..f867c5a
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_optimize_timing_recovery_parameters.m
@@ -0,0 +1,68 @@
+%% Fixe Simulationsparameter
+baud_rate = 6;
+power = 42.3;
+num_pf_coeff = 4;
+M = 4;
+trlength = 4096*2;
+
+%% === Suchräume für Tap-Werte (selbst bestimmbar) =================
+% WICHTIG: wähle Grenzen passend zur Skalierung deiner Taps.
+% Beispiel: FFE Taps vielleicht grob im Bereich [-500 .. 500],
+% DFE Taps eher kleiner, z.B. [-50 .. 50] – anpassen!
+ffeRange = [0, 500];
+dfeRange = [0, 200];
+
+vars = [
+ optimizableVariable('ffe1', ffeRange)
+ optimizableVariable('ffe2', ffeRange)
+ optimizableVariable('ffe3', ffeRange)
+
+ optimizableVariable('dfe1', dfeRange)
+ optimizableVariable('dfe2', dfeRange)
+ optimizableVariable('dfe3', dfeRange)
+];
+
+%% === Objective ===================================================
+objectiveFcn = @(x) objectiveWrapperTaps(x, ...
+ baud_rate, power, num_pf_coeff, M, trlength);
+
+%% === bayesopt ====================================================
+results = bayesopt(objectiveFcn, vars, ...
+ 'AcquisitionFunctionName','expected-improvement-plus', ...
+ 'MaxObjectiveEvaluations', 60, ...
+ 'IsObjectiveDeterministic', false, ...
+ 'ExplorationRatio', 0.5, ...
+ 'Verbose', 1, ...
+ 'PlotFcn', {@plotObjectiveModel,@plotMinObjective});
+
+%% === Beste Lösung ausgeben ======================================
+bestX = results.XAtMinObjective;
+bestBER = results.MinObjective;
+
+bestFFE = [bestX.ffe1, bestX.ffe2, bestX.ffe3];
+bestDFE = [bestX.dfe1, bestX.dfe2, bestX.dfe3];
+
+fprintf('\n=== Bestes Ergebnis ===\n');
+fprintf('BER : %.3e\n', bestBER);
+fprintf('taps_ffe : [%g %g %g]\n', bestFFE);
+fprintf('taps_dfe : [%g %g %g]\n\n', bestDFE);
+
+%% =================================================================
+function ber = objectiveWrapperTaps(x, baud_rate, power, num_pf_coeff, M, trlength)
+ taps_ffe = double([x.ffe1, x.ffe2, x.ffe3]);
+ taps_dfe = double([x.dfe1, x.dfe2, x.dfe3]);
+
+ % Optional: falls du z.B. Monotonicität / Struktur erzwingen willst, hier.
+ % taps_dfe(1) = 0; % Beispiel: ersten DFE-Tap fixieren
+
+ try
+ ber = first_analysis_optimize(baud_rate, power, num_pf_coeff, taps_ffe, taps_dfe, M, trlength);
+
+ if isempty(ber) || ~isscalar(ber) || ~isfinite(ber) || ber < 0
+ ber = 1; % Penalty
+ end
+ catch ME
+ warning("Objective failed: %s", ME.message);
+ ber = 1; % Penalty
+ end
+end
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_2.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_2.m
new file mode 100644
index 0000000..b0310bd
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_2.m
@@ -0,0 +1,66 @@
+%%
+current = 225:10:285;
+current = string(current);
+power = [28.02, 33.2, 37.5, 42.3, 46.3, 49.3, 53.4];
+power = string(power);
+num_pf_coeff = 4;
+taps_ffe = [200, 0, 0];
+taps_dfe = [0, 0, 0];
+M = 2;
+trlength = 4096*2;
+x = 225:10:285;
+BER_PAM_2 = [];
+post_only = 0;
+
+for eq_method = 2:4
+
+ if ~post_only
+ for j = 1:length(current)
+ BER_run = first_analysis_ber(current(j), power(j), num_pf_coeff, taps_ffe, taps_dfe, M, trlength, eq_method);
+ BER_PAM_2 = [BER_PAM_2, BER_run];
+ end
+
+ save('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_2_' + string(eq_method) + '.mat', 'x', 'BER_PAM_2')
+ end
+
+end
+
+%%
+x = 225:10:285;
+for k = 1:4
+ BER = load('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_2_' + string(k) + '.mat');
+ BER = BER.BER_PAM_2;
+
+ figure(202120)
+ plot(x, BER, '-o','LineWidth',1.75);
+ hold on
+end
+old_BER = [-1.5, -1.75, -2, -2.3, -2.6, -2.25, -1.95];
+old_BER = 10.^(old_BER);
+plot(x, old_BER, '-o','LineWidth',1.75)
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% Legende NUR für Kurven
+legend('FFE', 'FFE+PF+MLSE', 'DB', 'ML-MLSE', 'BER Paper', ...
+ 'Interpreter','latex', ...
+ 'Location','southwest', 'FontSize', 14)
+
+% FEC Labels direkt im Plot
+text(280,2.2e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(280,3.3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(280,5.4e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(280,2.4e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Laser Bias Current [mA]', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-2', 'Interpreter','latex')
+
+grid minor
+ylim([1e-4 5e-1])
+set(gca,'YScale','log')
+% beautifyBERplot
+hold off
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4.m
new file mode 100644
index 0000000..cf3b301
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4.m
@@ -0,0 +1,61 @@
+%%
+current = 225:10:285;
+current = string(current);
+power = [28.02, 33.2, 37.5, 42.3, 46.3, 49.3, 53.4];
+power = string(power);
+num_pf_coeff = 4;
+taps_ffe = [300, 0, 0];
+taps_dfe = [5, 0, 0];
+M = 4;
+trlength = 4096*4;
+x = 225:10:285;
+BER_PAM_4 = [];
+
+for eq_method = 2
+ for j = 4
+ BER_run = first_analysis_ber(current(j), power(j), num_pf_coeff, taps_ffe, taps_dfe, M, trlength, eq_method);
+ BER_PAM_4 = [BER_PAM_4, BER_run];
+ end
+ save('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_4_DFE_' + string(eq_method) + '.mat', 'x', 'BER_PAM_4')
+ BER_PAM_4 = [];
+end
+
+%%
+x = 225:10:285;
+for k = 1:4
+ BER = load('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\BER_PAM_4_' + string(k) + '.mat');
+ BER = BER.BER_PAM_4;
+
+ figure(202120)
+ plot(x, BER, '-o','LineWidth',1.75);
+ hold on
+end
+old_BER = [-1.6, -1.85, -2.2, -2.45, -2.3, -2, -1.4];
+old_BER = 10.^(old_BER);
+plot(x, old_BER, '-o','LineWidth',1.75)
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% Legende NUR für Kurven
+legend('FFE', 'FFE+PF+MLSE', 'DB', 'ML-MLSE', 'BER Paper', ...
+ 'Interpreter','latex', ...
+ 'Location','southwest', 'FontSize', 14)
+
+% FEC Labels direkt im Plot
+text(286,2.2e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(285,3.3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(282.5,5.4e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(287,2.4e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Laser Bias Current [mA]', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-4', 'Interpreter','latex')
+
+grid minor
+ylim([1e-4 5e-1])
+set(gca,'YScale','log')
+% beautifyBERplot
+hold off
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4_baud_rate_sweep.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4_baud_rate_sweep.m
new file mode 100644
index 0000000..0575787
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_pam_4_baud_rate_sweep.m
@@ -0,0 +1,77 @@
+baud_rate = 4:1:8;
+baud_rate = string(baud_rate);
+power = [8.4, 8.4, 42.3, 8.4, 8.4];
+power = string(power);
+num_pf_coeff = 4;
+taps_ffe = [300, 0, 0];
+taps_dfe = [0, 0, 0];
+M = 4;
+trlength = 4096*4;
+x = 4:1:8;
+BER_PAM_4 = [];
+Alpha_PAM_4 = [];
+
+for method = 1:4
+ for i = 1:5
+ [BER_run, ~] = first_analysis_baud_rate_sweep(baud_rate(i), power(i), num_pf_coeff, taps_ffe, taps_dfe, M, trlength, method);
+ BER_PAM_4 = [BER_PAM_4, BER_run];
+ % Alpha_PAM_4 = [Alpha_PAM_4, Alpha_run];
+ end
+ save('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\New Baud Rate Sweep Data\BER_PAM_4_' + string(method) + '.mat', 'x', 'BER_PAM_4')
+ BER_PAM_4 = [];
+end
+
+%% BER
+for k = 1:4
+ BER = load('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\New Baud Rate Sweep Data\BER_PAM_4_' + string(k) + '.mat');
+ BER = BER.BER_PAM_4;
+ % x = BER.x;
+ % BER_Alpha = load(filename);
+ % BER = BER_Alpha.BER_PAM_4;
+
+ % close all
+ figure(239)
+ plot(x, BER, '-o','LineWidth',1.75)
+ hold on
+end
+
+% old_BER = [-4.4, -4, -3.3, -2.7, -2.5, -2.475];
+% old_BER = 10.^(old_BER);
+% plot(x, old_BER, '--o','LineWidth',1)
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% Legende NUR für Kurven
+legend('FFE','FFE+PF+MLSE','DB','ML-MLSE',...
+ 'Interpreter','latex', ...
+ 'Location','southwest', 'FontSize', 14)
+
+% FEC Labels direkt im Plot
+text(8.2,2.3e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(8.2,3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(8.2,5.6e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(8.2,2.5e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Symbol Rate [GBd]', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-4', 'Interpreter','latex')
+
+grid minor
+% ylim([1e-4 5e-2])
+xlim([3 9])
+set(gca,'YScale','log')
+hold off
+
+%% Channel Alpha
+% Alpha = BER_Alpha.Alpha_PAM_4;
+% figure
+% plot(x, Alpha, '--o','LineWidth',1)
+% hold on
+% xlabel('Symbol Rate [GBd]', 'Interpreter','latex')
+% ylabel('Channel Alpha', 'Interpreter','latex')
+% title('Channel Alpha for PAM-4 - 300-Tap-FFE - 1 Postfilter Coefficients - 8192 Training Symbols', 'Interpreter','latex')
+% grid minor
+% hold off
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/evalscript_timing_shift.m b/projects/FSO_transmission/Evaluation Scripts/evalscript_timing_shift.m
new file mode 100644
index 0000000..435b540
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/evalscript_timing_shift.m
@@ -0,0 +1,9 @@
+time_shift = -2:0.1:2;
+BER = zeros(1,length(time_shift));
+for i = 1:length(time_shift)
+ BER_value = first_analysis_time_shift(time_shift(i));
+ BER(i) = BER_value;
+ close all
+end
+figure;
+plot(time_shift,BER)
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_2.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_2.m
new file mode 100644
index 0000000..c3a44f0
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_2.m
@@ -0,0 +1,190 @@
+function BER_value = first_analysis_2(taps_ffe, taps_dfe, trlen, trloops, ddloops, K_FFE, DCmu, DDmu, DFEmu, FFEmu, plot_final, idealdfe, num_pf_coeffs, damp_factor, norm_loop_bw, det_gain)
+ %%
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+ mode = 0; %0 oder 1
+ M = 2;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+
+ % Rx_Time_Rec = Rx_matched;
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2, ...
+ 'damping_factor',damp_factor,'normalized_loop_bandwidth',norm_loop_bw,'detector_gain',det_gain).process(Rx_matched_1);
+ figure;plot(Timing_Error);
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 28e9, 14e9);
+
+ %% not working..
+ Rx_synced = Rx_Time_Rec;
+ Rx_synced.fs = 14e9;
+ % Rx_synced = Rx_synced_cell{1};
+ % len_tr = trlen;
+ % mu_ffe1 = mu_ffe_values(1);
+ % mu_ffe2 = mu_ffe_values(2);
+ % mu_ffe3 = mu_ffe_values(3);
+ % mu_dc = 0.005;
+ % mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ % mu_dfe = mu_dfe_values;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',2);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',0);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',0);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',0);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ % "training_length",trlen,"training_loops",trloops,"dd_loops",ddloops, ...
+ % "K",K_FFE,"DCmu",DCmu,"DDmu",DDmu,"DFEmu",DFEmu, ...
+ % "FFEmu",FFEmu,"plotfinal",plot_final,"ideal_dfe",idealdfe);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % % ffe_results.metrics.print
+ % fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % BER_value = ffe_results.metrics.BER;
+
+ %% -------------------- VNLE + MLSE --------------------
+
+ pf_ncoeffs = num_pf_coeffs;
+ eq_v = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ "training_length",trlen,"training_loops",trloops,"dd_loops",ddloops, ...
+ "K",K_FFE,"DCmu",DCmu,"DDmu",DDmu,"DFEmu",DFEmu, ...
+ "FFEmu",FFEmu,"plotfinal",plot_final,"ideal_dfe",idealdfe);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = mlse_results.metrics.BER;
+
+ %% -------------------- DB target --------------------
+ % mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+ %
+ % eq_ = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ % "training_length",trlen,"training_loops",trloops,"dd_loops",ddloops, ...
+ % "K",K_FFE,"DCmu",DCmu,"DDmu",DDmu,"DFEmu",DFEmu, ...
+ % "FFEmu",FFEmu,"plotfinal",plot_final,"ideal_dfe",idealdfe);
+ %
+ % dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ % "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+ %
+ % dbt_results.metrics.print("description",'Duobinary');
+ % mlse_results.metrics.print
+ % fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced)/2,"mu_dd",0.03,"mu_tr",0.03,"order",30,"sps",1, ...
+ % "traceback_depth",256,"L",2,"delta",10,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % fprintf('ML-based MLSE:');
+ % fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ %
+ % % -------------------- Post-FFE --------------------
+ % eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",1e-4,"mu_tr",0,"order",1001,"sps",1,"decide",0);
+ % post_ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ % fprintf('Post-FFE:');
+ % fprintf('My EQ: %.1e \n',post_ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_baud_rate_sweep.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_baud_rate_sweep.m
new file mode 100644
index 0000000..0fe859c
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_baud_rate_sweep.m
@@ -0,0 +1,202 @@
+function [BER, Channel_Alpha] = first_analysis_baud_rate_sweep(baud_rate, power, num_pf_coeff, taps_ffe, taps_dfe, M, trlength, method)
+ %%
+ close all
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\Sweep Data\";
+ mode = 0; %0 oder 1
+ % M = 2;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=" + current + "mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, baud_rate + "G_PAM4\tx_info\tx_info_PAM4_" + baud_rate + "Gbd0.6RRC.mat");
+ filename = fullfile(base, baud_rate + "G_PAM4\M=4_Rs="+ baud_rate + "e9_Fs=8e10_I=255mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ Kov = 14;
+
+ Scope_sig = Scope_sig.resample('fs_in',fs,'fs_out',Kov*fsym);
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+
+ % Rx_Time_Rec = Rx_matched;
+ % Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+
+ Time_Rec = 1;
+ if Time_Rec
+ Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*128,'sps',Kov,'comp_signal',0,'comp_mode',0).process(Rx_matched_1);
+ sps = 1;
+ else
+ sps = 2;
+ end
+
+ Rx_Time_Rec.fs = fsym;
+
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 12e9, 6e9);
+
+ % Rx_matched_1.plot("fignum",231231)
+ % Rx_Time_Rec.plot("fignum",231231)
+
+ %% not working..
+ Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = trlength;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ if method == 1
+ %% -------------------- FFE --------------------
+ % requires some more digging what is going on :-)
+ eq_ffe = EQ("Ne",[500, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+ ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ "eth_style_symbol_mapping",mapping_style);
+
+ % ffe_results.metrics.print
+ fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = ffe_results.metrics.BER;
+
+ elseif method == 2
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = num_pf_coeff;
+ eq_v = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = mlse_results.metrics.BER;
+ % Channel_Alpha = mlse_results.metrics.Alpha;
+
+ elseif method == 3
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ ml_mlse_equalizer = ML_MLSE("epochs_tr",150,"epochs_dd",1, ...
+ "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",80,"sps",sps, ...
+ "traceback_depth",256,"L",1,"delta",4,"adaptive_mu",0);
+
+ [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ fprintf('ML-based MLSE:\n');
+ fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = ml_mlse_results.metrics.BER;
+
+ elseif method == 4
+ %% -------------------- DB target --------------------
+ mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+
+ eq_ = EQ("Ne",taps_ffe,"Nb",taps_dfe,"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+ dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+
+ dbt_results.metrics.print("description",'Duobinary');
+ fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = dbt_results.metrics.BER;
+ end
+ Channel_Alpha = 0;
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_ber.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_ber.m
new file mode 100644
index 0000000..bc02b58
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_ber.m
@@ -0,0 +1,207 @@
+function BER_value = first_analysis_ber(current, power, num_pf_coeff, taps_ffe, taps_dfe, M, trlength, eq_method)
+ %%
+ close all
+
+ %%
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+ mode = 0; %0 oder 1
+ % M = 2;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=" + current + "mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=" + current + "mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ Kov = 14;
+
+ Scope_sig = Scope_sig.resample('fs_in', fs, 'fs_out', Kov*fsym);
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+
+ % Rx_Time_Rec = Rx_matched;
+ % Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+
+ Time_Rec = 1;
+ if Time_Rec
+ Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*128,'sps',Kov,'comp_signal',0,'comp_mode',0).process(Rx_matched_1);
+ sps = 1;
+ else
+ sps = Kov;
+ end
+ Rx_Time_Rec.fs = fsym;
+
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 12e9, 6e9);
+
+ % Rx_matched_1.plot("fignum",231231)
+ % Rx_Time_Rec.plot("fignum",231231)
+
+ %% not working..
+ Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = trlength;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ if eq_method == 1
+
+ %% -------------------- FFE --------------------
+ % requires some more digging what is going on :-)
+ eq_ffe = EQ("Ne",[500, 0, 0],"Nb",[0,0,0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+ ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ "eth_style_symbol_mapping",mapping_style);
+
+ % ffe_results.metrics.print
+ fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = ffe_results.metrics.BER;
+
+ elseif eq_method == 2
+
+ %% -------------------- VNLE + MLSE --------------------
+
+ pf_ncoeffs = num_pf_coeff;
+ eq_v = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = mlse_results.metrics.BER;
+
+ elseif eq_method == 3
+
+ %% -------------------- DB target --------------------
+ mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+
+ eq_ = EQ("Ne",taps_ffe,"Nb",taps_dfe,"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+ dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+
+ dbt_results.metrics.print("description",'Duobinary');
+ fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = dbt_results.metrics.BER;
+
+ elseif eq_method == 4
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ ml_mlse_equalizer = ML_MLSE("epochs_tr",150,"epochs_dd",1, ...
+ "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",80,"sps",1, ...
+ "traceback_depth",256,"L",1,"delta",4,"adaptive_mu",0);
+
+ [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ fprintf('ML-based MLSE:\n');
+ fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER_value = ml_mlse_results.metrics.BER;
+
+ end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_mf.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_mf.m
new file mode 100644
index 0000000..b2a6027
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_mf.m
@@ -0,0 +1,293 @@
+
+<<<<<<< HEAD
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+=======
+base = "C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC";
+>>>>>>> 719e5508e776c18e3dcf72a954c71bdedda179e0
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+<<<<<<< HEAD
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+=======
+ tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+>>>>>>> 719e5508e776c18e3dcf72a954c71bdedda179e0
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_tx_compare = Pform.process(Symbols);
+
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sync = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_tx_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+timesig_compare = [0:1:datas.tr.lastData(1).trace.ch3.Points-1] ./ fs;
+timesig = datas.tr.lastData(1).trace.ch3.XData;
+assert(isequal(traceData.YData,scoperead_volts));
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+%%
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef directly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+apply_matched_filter = 0;
+k = 4;
+if apply_matched_filter
+
+ Pform = Pulseformer("fsym",fsym,"fdac",k*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Pform.process(Scope_sig);
+else
+
+ Rx_matched = Filter('filtdegree',4,"f_cutoff",fsym*0.5,"fs",Scope_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scope_sig);
+ Rx_matched = Rx_matched.resample("fs_out",k*fsym);
+end
+Rx_matched.spectrum();
+
+<<<<<<< HEAD
+%%
+
+sys = comm.SymbolSynchronizer('TimingErrorDetector', 'Gardner (non-data-aided)', ...
+ 'SamplesPerSymbol', 2, ...
+ 'DampingFactor', 0.7, ...
+ 'NormalizedLoopBandwidth', 0.01);
+Rx_symbolsync = Rx_matched;
+[Rx_symbolsync.signal, timing_error] = sys(Rx_matched.signal);
+
+plot(timing_error); % If this is a ramp, you have drift!
+
+%% timing sync -> at this point we still have no symbol timing recovery, we
+% % try to do this with 2sps EQ!
+
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+% Rx_matched_1 = Rx_synced_cell{1};
+%
+% % Rx_Time_Rec = Rx_matched;
+% [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+% figure;plot(Timing_Error);
+% % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 28e9, 14e9);
+
+%% not working..
+% Rx_synced = Rx_Time_Rec;
+Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*2;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.005;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',2);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',0);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',0);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',0);
+=======
+%% Timing Rec
+apply_timing_rec = 1;
+if apply_timing_rec
+ [Rx_symbolsync, timing_error] = Timing_Recovery("timing_error_detector",'Gardner (non-data-aided)','sps',k,'damping_factor',0.1,'normalized_loop_bandwidth',0.1,'detector_gain',2.7).process(Rx_matched);
+ figure();plot(timing_error);
+ Rx_symbolsync.fs = fsym;
+ % Tsynch
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
+ Rx_synced = Rx_synced_cell{1};
+ sps = 1;
+else
+ % Tsynch
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
+ Rx_synced = Rx_synced_cell{1};
+ Rx_synced = Rx_synced.resample("fs_out",2*fsym);
+ sps = 2;
+end
+>>>>>>> 719e5508e776c18e3dcf72a954c71bdedda179e0
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+
+%%
+
+Rx_synced = Rx_synced_cell{1};
+
+
+% -------------------- FFE --------------------
+% requires some more digging what is going on :-)
+eq_ffe = EQ("Ne",[50, 1, 1],"Nb",[2,0,0], ...
+ "training_length",512,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+vars = logspace(-4,-3,36);
+
+parfor i = 1:numel(vars)
+
+ len_tr = 4096;
+ mu_ffe1 = 0.01;% mus(i);%0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.005;
+ mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
+ mu_dfe = vars(i);
+ duob_mode = db_mode.no_db;
+
+ % requires some more digging what is going on :-)
+ eq_ffe_1 = EQ("Ne",[150, 1, 0],"Nb",[50,0,0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",0);
+
+ eq_ffe_2 = FFE("epochs_tr",1,"epochs_dd",vars(i),"len_tr",4096,"mu_dd",vars(i),"mu_tr",vars(i),"order",999,"sps",1,"decide",0, "adaption",adaption_method.nlms,"dd_mode",0);
+ % eq_ffe_2 = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-5,"dfe_mu_dd",mus(i),"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",50,"dfe_order",10,"sps",1,"decide",1);
+
+
+ ffe_results = ffe(eq_ffe_1,M,Rx_synced,Symbols,Bits, ...
+ "precode_mode",duob_mode,'showAnalysis',1,"postFFE",[], ...
+ "eth_style_symbol_mapping",mapping_style);
+
+ ffe_results.metrics.BER
+ bers(i) = ffe_results.metrics.BER;
+end
+
+figure();
+plot(vars,bers);
+yline(ber_in_paper)
+beautifyBERplot();
+%
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+ffe_results.metrics.print("description",'FFE');
+fprintf('FFE: %.1e \n',ffe_results.metrics.BER);
+
+
+%% -------------------- VNLE + MLSE --------------------
+len_tr = 4096;
+mu_ffe1 = 0.0001;% mus(i);%0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.005;
+mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+
+pf_ncoeffs = 4;
+
+vars = 1:7;
+bers = zeros(size(vars));
+parfor i = 1:numel(vars)
+
+ eqv = EQ("Ne",[200, 1, 0],"Nb",[2, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ncoeffs = vars(i);
+ pf = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eqv, pf, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ vnle_results.metrics.print("description",'VNLE');
+ mlse_results.metrics.print("description",'MLSE');
+ bers(i) = mlse_results.metrics.BER;
+end
+%%
+figure();hold on
+plot(vars,bers_ffe,'DisplayName','FFE [200,0,0] + PF + MLSE');
+plot(vars,bers_vnle,'DisplayName','VNLE [200,1,0] + PF + MLSE');
+plot(vars,bers_vnledfe,'DisplayName','VNLE [200,1,0] + DFE [2] + PF + MLSE');
+plot(vars,bers_vnledfe_ideal,'DisplayName','VNLE [200,1,0] + ideal DFE [2] + PF + MLSE');
+yline(ber_in_paper);
+yline([2e-2, 4.85e-3, 3.8e-3, 2,2e-4],'LineWidth',2,'Color',[0.8,0.8,0.8],'LineStyle',':','HandleVisibility','off');
+ylim([1e-5,0.1]);
+beautifyBERplot();
+
+%% -------------------- DB target --------------------
+mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+
+eq_ = EQ("Ne",[50, 5, 5],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+dbt_results.metrics.print("description",'Duobinary');
+
+
+%%
+
+%ML-based MLSE (L=2)
+mu_ml = 0.01; training_epochs = 100;
+ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
+ "len_tr",len_tr,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",1, ...
+ "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0);
+
+[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode);
+ml_mlse_results.metrics.print("description",'ML ');
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_optimize.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_optimize.m
new file mode 100644
index 0000000..5d5b4f9
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_optimize.m
@@ -0,0 +1,177 @@
+function BER = first_analysis_optimize(baud_rate, power, num_pf_coeff, taps_ffe, taps_dfe, M, trlength)
+ %%
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\Sweep Data\";
+ mode = 0; %0 oder 1
+ % M = 2;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=" + current + "mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, baud_rate + "G_PAM4\tx_info\tx_info_PAM4_" + baud_rate + "Gbd0.6RRC.mat");
+ filename = fullfile(base, baud_rate + "G_PAM4\M=4_Rs="+ baud_rate + "e9_Fs=8e10_I=255mA_RoP=" + power + "mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+
+ % Rx_Time_Rec = Rx_matched;
+ % Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+
+ Time_Rec = 1;
+ if Time_Rec
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',0.0123793,'normalized_loop_bandwidth',4.50865e-06,'detector_gain',3.33311).process(Rx_matched_1);
+ sps = 1;
+ else
+ sps = 2;
+ end
+
+ baud_rate_num = str2double(baud_rate);
+ Rx_Time_Rec.fs = baud_rate_num*10^9;
+
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 12e9, 6e9);
+
+ % Rx_matched_1.plot("fignum",231231)
+ % Rx_Time_Rec.plot("fignum",231231)
+
+ %% not working..
+ Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = trlength;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[400, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % % ffe_results.metrics.print
+ % fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % BER_value = ffe_results.metrics.BER;
+
+ %% -------------------- VNLE + MLSE --------------------
+
+ pf_ncoeffs = num_pf_coeff;
+ eq_v = EQ("Ne",taps_ffe,"Nb",taps_dfe, ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ BER = mlse_results.metrics.BER;
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",150,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",80,"sps",1, ...
+ % "traceback_depth",256,"L",1,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % fprintf('ML-based MLSE:\n');
+ % fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec.m
new file mode 100644
index 0000000..22622ec
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec.m
@@ -0,0 +1,160 @@
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 4;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+% Rx_Time_Rec = Rx_matched;
+% Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+[Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+
+%% not working..
+Rx_synced = Rx_Time_Rec;
+% Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*2;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.004;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+sps = 1;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+%% -------------------- FFE --------------------
+% % requires some more digging what is going on :-)
+% eq_ffe = EQ("Ne",[50, 5, 5],"Nb",[2,0,0], ...
+% "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+% "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+% "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+% "eth_style_symbol_mapping",mapping_style);
+%
+% % ffe_results.metrics.print
+% fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+% BER_value = ffe_results.metrics.BER;
+
+%% -------------------- VNLE + MLSE --------------------
+
+pf_ncoeffs = 1;
+eq_v = EQ("Ne",[200, 3, 2],"Nb",[5, 2, 1], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+mlse_results.metrics.print
+fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- ML-based MLSE (L=2) --------------------
+% ml_mlse_equalizer = ML_MLSE("epochs_tr",150,"epochs_dd",1, ...
+% "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",80,"sps",1, ...
+% "traceback_depth",256,"L",1,"delta",4,"adaptive_mu",0);
+%
+% [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+% fprintf('ML-based MLSE:\n');
+% fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam2.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam2.m
new file mode 100644
index 0000000..e681f2e
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam2.m
@@ -0,0 +1,171 @@
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+Time_Rec = 1;
+if Time_Rec
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+ sps = 1;
+else
+ sps = 2;
+end
+
+if Time_Rec == 1 && M == 2
+ Rx_Time_Rec.fs = 14e9;
+elseif Time_Rec == 1 && M == 4
+ Rx_Time_Rec.fs = 6e9;
+end
+
+%% not working..
+Rx_synced = Rx_Time_Rec;
+% Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*4;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.004;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+sps = 1;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+%% -------------------- FFE --------------------
+% % requires some more digging what is going on :-)
+% eq_ffe = EQ("Ne",[200, 0, 0],"Nb",[0,0,0], ...
+% "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+% "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+% "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+% "eth_style_symbol_mapping",mapping_style);
+%
+% % ffe_results.metrics.print
+% fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+% BER_value = ffe_results.metrics.BER;
+
+%% -------------------- VNLE + MLSE --------------------
+
+pf_ncoeffs = 4;
+eq_v = EQ("Ne",[250, 0, 0],"Nb",[7, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",0, ...
+ 'weighted_DFE',1,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','I2','weighted_DFE_I_mode',[5,0.5,0.6]);
+pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+mlse_results.metrics.print
+fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- ML-based MLSE (L=2) --------------------
+% ml_mlse_equalizer = ML_MLSE("epochs_tr",400,"epochs_dd",1, ...
+% "len_tr",len_tr,"mu_dd",0.03,"mu_tr",0.03,"order",100,"sps",sps, ...
+% "traceback_depth",256,"L",2,"delta",4,"adaptive_mu",0);
+%
+% [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+% fprintf('ML-based MLSE:\n');
+% fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam4.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam4.m
new file mode 100644
index 0000000..2881ccf
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_rec_pam4.m
@@ -0,0 +1,189 @@
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\Sweep Data\";
+mode = 0; %0 oder 1
+M = 4;
+
+baud_rate = '8';
+baud_rate_num = str2double(baud_rate);
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data_path = fullfile(base, baud_rate + "G_PAM2\tx_info\tx_info_PAM2_" + baud_rate + "Gbd0.75RRC.mat");
+ filename = fullfile(base, baud_rate + "G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, baud_rate + "G_PAM4\tx_info\tx_info_PAM4_" + baud_rate + "Gbd0.6RRC.mat");
+ filename = fullfile(base, baud_rate + "G_PAM4\M=4_Rs=" + baud_rate + "e9_Fs=8e10_I=255mA_RoP=8.4mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+% Rx_Time_Rec = Rx_matched;
+% Rx_Time_Rec = Timing_Recovery_Move_It('f_sim', 28e9, 'gamma', 0.1).process(Rx_matched_1);
+
+Time_Rec = 1;
+if Time_Rec
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+ sps = 1;
+else
+ sps = 2;
+end
+
+Rx_Time_Rec.fs = baud_rate_num * 10^9;
+
+% Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal, 12e9, 6e9);
+
+% Rx_matched_1.plot("fignum",231231)
+% Rx_Time_Rec.plot("fignum",231231)
+
+%% not working..
+Rx_synced = Rx_Time_Rec;
+% Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*2;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.004;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+%% -------------------- FFE --------------------
+% % requires some more digging what is going on :-)
+% eq_ffe = EQ("Ne",[400, 0, 0],"Nb",[0,0,0], ...
+% "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+% "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+% "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+% "eth_style_symbol_mapping",mapping_style);
+%
+% % ffe_results.metrics.print
+% fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+% BER_value = ffe_results.metrics.BER;
+
+%% -------------------- VNLE + MLSE --------------------
+
+pf_ncoeffs = 4;
+eq_v = EQ("Ne",[300, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+mlse_results.metrics.print
+fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- ML-based MLSE (L=2) --------------------
+% ml_mlse_equalizer = ML_MLSE("epochs_tr",200,"epochs_dd",1, ...
+% "len_tr",length(Rx_synced)/2,"mu_dd",0,"mu_tr",0.03,"order",100,"sps",sps, ...
+% "traceback_depth",256,"L",3,"delta",4,"adaptive_mu",0);
+%
+% [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+% fprintf('ML-based MLSE:\n');
+% fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- DB target --------------------
+% mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+%
+% eq_ = EQ("Ne",[50, 5, 5],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+% "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+%
+% dbt_results.metrics.print("description",'Duobinary');
+% fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_shift.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_shift.m
new file mode 100644
index 0000000..162d6a6
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_time_shift.m
@@ -0,0 +1,221 @@
+function BER = first_analysis_time_shift(time_shift)
+ %%
+ base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+ mode = 0; %0 oder 1
+ M = 4;
+
+ all_files = dir(fullfile(base, "**/*.mat"));
+
+ % data_tr_mf is the already recovered and filtered data
+ if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ end
+
+ if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+ end
+
+ tx_data = load(tx_data_path);
+ datas = load(filename);
+
+ %%
+ str = filename;
+ M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+ assert(M==M_);
+ fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+ fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+ I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+ rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+ L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+ pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+ rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+ mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+ %%
+ % Tx data
+
+ Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+ Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+ mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+ PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+ Symbols_ = PM.map(Bits) .* PM.scaling;
+ assert(isequal(Symbols.signal,Symbols_.signal));
+
+ Bits_ = PM.demap(Symbols);
+ [bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+ assert(ber == 0);
+
+ %% For comparison, apply pulsef on Tx Symbols
+ Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Digi_sig_compare = Pform.process(Symbols);
+ MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+ Rx_sig_compare = MF.process(Digi_sig_compare);
+
+ %%
+
+ % Rx Data
+ traceData = datas.tr.lastData(2).trace.ch3;
+
+ %FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+ demystified = isequal(traceData.YData,scoperead_volts);
+ assert(demystified);
+
+ Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+ Scope_sig.plot("displayname",'raw','fignum',100);
+ Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+ % 1) matched filter
+ % pulse is symmetric, hence we can use pulsef firectly as matched filter.
+ % It feels off (bit I think correct) that the fsym is now the output freq.!!
+ % -> output 2 sps to omit timing recovery!?
+ Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Matched_Filter.process(Scope_sig);
+ Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+ data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+
+ % timing sync -> at this point we still have no symbol timing recovery, we
+ % try to do this with 2sps EQ!
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_matched_1 = Rx_synced_cell{1};
+ Rx_matched_original = Rx_synced_cell{1};
+ Rx_matched_original.signal = resample(Rx_matched_original.signal,1,2);
+
+ [~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+ Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+ Time_Rec = 1;
+ if Time_Rec
+ % [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',1e-4,'detector_gain',2.7).process(Rx_matched_1);
+ Rx_Time_Rec = Time_Shifter('value',time_shift).process(Rx_matched_1);
+ Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal,1,2);
+ % figure(2222222)
+ % plot(Timing_Error)
+ sps = 1;
+ else
+ sps = 2;
+ end
+
+ if Time_Rec == 1 && M == 2
+ Rx_Time_Rec.fs = 14e9;
+ elseif Time_Rec == 1 && M == 4
+ Rx_Time_Rec.fs = 6e9;
+ end
+
+ Rx_Time_Rec.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+ % Rx_matched_original.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+ Rx_tr_mf.spectrum('normalizeTo0dB',1,"displayname",'Their signal','fignum',101111);
+
+ Rx_Time_Rec_plot = Rx_Time_Rec;
+ % Rx_matched_original_plot = Rx_matched_original;
+ Rx_tr_mf_plot = Rx_tr_mf;
+ Rx_Time_Rec_plot.normalize("mode","rms").plot("displayname",'Our signal','fignum',101311);
+ % Rx_matched_original_plot.normalize("mode","rms").plot("displayname",'Original signal','fignum',101311);
+ Rx_tr_mf_plot.normalize("mode","rms").plot("displayname",'Their signal','fignum',101311);
+
+ %% not working..
+ % Use our or their signal
+ for our_signal = 1
+ if our_signal
+ Rx_synced = Rx_Time_Rec;
+ else
+ Rx_synced = Rx_tr_mf;
+ end
+ % Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = 4096*2;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[500, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % % eq_ffe = FFE_DFE('ffe_order',99,'dfe_order',99,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5,'sps',sps,'decide',0);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % if our_signal
+ % fprintf('Our signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = 4;
+ eq_v = EQ("Ne",[100, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','R2','weighted_DFE_I_mode',[5,0.5,0.6]);
+ % eq_v = FFE_DFE('ffe_order',200,'dfe_order',0,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5, ...
+ % 'ffe_mu_dd',mu_ffe,'ffe_mu_tr',0,'dfe_mu_dd',mu_dfe,'dfe_mu_tr',0.005,'sps',sps,'decide',0);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ if our_signal
+ fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ else
+ fprintf('Their Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ end
+ BER = mlse_results.metrics.BER;
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",11,"sps",sps, ...
+ % "traceback_depth",256,"L",4,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % if our_signal
+ % fprintf('Our Signal: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+ end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m
new file mode 100644
index 0000000..fe8f371
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m
@@ -0,0 +1,271 @@
+%%
+clear all;
+close all;
+
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 4;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+% data_tr_mf is the already recovered and filtered data
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+%Calculate Transfer Function
+Tx_spectrum = Digi_sig_compare;
+Rx_spectrum = Scope_sig;
+
+[~,Rx_synced_spectrum,inverted_spectrum,sequenceFound_spectrum,sequenceStarts_spectrum] = Rx_spectrum.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_spectrum = Rx_synced_spectrum{1};
+
+Tx_spectrum = Tx_spectrum.resample("fs_out",fsym);
+Tx_spectrum.signal = fft(Tx_spectrum.signal);
+
+Rx_spectrum = Rx_spectrum.resample("fs_out",fsym);
+Rx_spectrum.signal = fft(Rx_spectrum.signal);
+
+H_transfer = Rx_spectrum.signal./Tx_spectrum.signal;
+H_inv = 1./H_transfer;
+
+%Number of Samples/Symbol after Matched Filter
+Kov = 14;
+
+Scope_sig = Scope_sig.resample('fs_in',fs,'fs_out',Kov*fsym);
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+% Rx_matched = Scope_sig;
+% Rx_matched = Rx_matched.resample('fs_out',Kov*fsym);
+
+data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+[~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+
+% Rx_matched = Scope_sig;
+% Rx_matched = Rx_matched.resample("fs_out",2*fsym);
+
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+Rx_matched_original = Rx_synced_cell{1};
+Rx_matched_original.signal = resample(Rx_matched_original.signal,1,Kov);
+Rx_matched_original.fs = fsym;
+
+% Rx_matched_1 = Rx_matched_1.resample("fs_in",Kov*fsym,'fs_out',2*fsym);
+
+Time_Rec = 1;
+if Time_Rec
+ % [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',Kov,'damping_factor',1,'normalized_loop_bandwidth',1e-4,'detector_gain',2.7).process(Rx_matched_1);
+
+ [Rx_Time_Rec, Timing_Error_MG] = Godard_Timing_Recovery('mode',3,'num_blocks',1,'fft_length',length(Rx_matched_1),'sps',Kov,'rolloff',0.6,'mu',-0.2,'Ki',1e-4).process(Rx_matched_1);
+ Rx_Time_Rec = Rx_Time_Rec.resample('fs_in',Kov*fsym,'fs_out',fsym);
+
+ % Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*128,'sps',Kov,'comp_signal',Rx_tr_mf,'comp_mode',0).process(Rx_matched_1);
+
+ sps = 1;
+else
+ Rx_Time_Rec = Rx_matched_1;
+ sps = 1;
+end
+
+Rx_Time_Rec.fs = fsym;
+
+% h = gaussdesign(0.4,64,1);
+% Rx_Time_Rec.signal = filtfilt(h,1,Rx_Time_Rec.signal);
+
+% Rx_com = Rx_Time_Rec;
+% Rx_com = Rx_com.resample('fs_in',fsym,'fs_out',2*fsym);
+% Rx_com.spectrum("displayname",'After TR','normalizeTo0dB',1,'fignum',101114);
+% Rx_matched_1.spectrum("displayname",'Before TR','normalizeTo0dB',1,'fignum',101114);
+% Rx_tr_mf_2sps = Rx_tr_mf;
+% Rx_tr_mf_2sps = Rx_tr_mf_2sps.resample('fs_in',fsym,'fs_out',2*fsym);
+% Rx_tr_mf_2sps.spectrum("displayname",'Their signal after TR','normalizeTo0dB',1,'fignum',101114);
+
+% Rx_Time_Rec = Rx_Time_Rec.resample('fs_out',6e9);
+% Rx_tr_mf = Rx_tr_mf.resample('fs_out',fsym);
+
+% Amax = 10;
+% H_inv = min(abs(H_inv), Amax) .* exp(1j*angle(H_inv));
+% Rx_Time_Rec.signal = fft(Rx_Time_Rec.signal);
+% Rx_Time_Rec.signal = ifft(Rx_Time_Rec.signal .* H_inv);
+
+Rx_Time_Rec = Rx_Time_Rec.normalize('mode','rms');
+Rx_tr_mf = Rx_tr_mf.normalize('mode','rms');
+
+Rx_Time_Rec.spectrum("displayname",'Our signal','normalizeTo0dB',1,'fignum',101111);
+% Rx_matched_original.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+Rx_tr_mf.spectrum("displayname",'Their signal','normalizeTo0dB',1,'fignum',101111);
+
+Rx_Time_Rec.normalize("mode","rms").plot("displayname",'Our signal','fignum',101311);
+% Rx_matched_original.normalize("mode","rms").plot("displayname",'Original signal','fignum',101311);
+Rx_tr_mf.normalize("mode","rms").plot("displayname",'Their signal','fignum',101311);
+
+%% not working..
+% Use our or their signal
+for our_signal = 1
+ if our_signal
+ Rx_synced = Rx_Time_Rec;
+ else
+ Rx_synced = Rx_tr_mf;
+ end
+ % Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = 4096*2;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[150, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % % eq_ffe = FFE_DFE('ffe_order',99,'dfe_order',99,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5,'sps',sps,'decide',0);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % if our_signal
+ % fprintf('Our signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = 4;
+ eq_v = EQ("Ne",[300, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','R2','weighted_DFE_I_mode',[5,0.5,0.6]);
+ % eq_v = FFE_DFE('ffe_order',300,'dfe_order',5,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5, ...
+ % 'ffe_mu_dd',mu_ffe,'ffe_mu_tr',0,'dfe_mu_dd',mu_dfe,'dfe_mu_tr',0.005,'sps',sps,'decide',0);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ if our_signal
+ fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ else
+ fprintf('Their Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ end
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",11,"sps",sps, ...
+ % "traceback_depth",256,"L",4,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % if our_signal
+ % fprintf('Our Signal: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam2.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam2.m
new file mode 100644
index 0000000..e6f4c45
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam2.m
@@ -0,0 +1,195 @@
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ % Already recovered and filtered data
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+[~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+
+Time_Rec = 1;
+if Time_Rec
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',2,'damping_factor',1,'normalized_loop_bandwidth',0.01,'detector_gain',2.7).process(Rx_matched_1);
+ sps = 1;
+else
+ sps = 2;
+end
+
+if Time_Rec == 1 && M == 2
+ Rx_Time_Rec.fs = 14e9;
+elseif Time_Rec == 1 && M == 4
+ Rx_Time_Rec.fs = 6e9;
+end
+
+Rx_Time_Rec.spectrum("displayname",'Signal after matched filter, synchronization, and timing recovery','fignum',101111);
+Rx_tr_mf.spectrum("displayname",'Signal after matched filter, synchronization, and timing recovery','fignum',101111);
+
+Rx_Time_Rec_plot = Rx_Time_Rec;
+Rx_tr_mf_plot = Rx_tr_mf;
+Rx_Time_Rec_plot.normalize("mode","rms").plot("displayname",'Signal after matched filter, synchronization, and timing recovery','fignum',101311);
+Rx_tr_mf_plot.normalize("mode","rms").plot("displayname",'Signal after matched filter, synchronization, and timing recovery','fignum',101311);
+
+%% not working..
+% Use our or their signal
+our_signal = 0;
+if our_signal
+ Rx_synced = Rx_Time_Rec;
+else
+ Rx_synced = Rx_tr_mf;
+end
+% Rx_synced = Rx_Time_Rec;
+% Rx_synced = Rx_synced_cell{1};
+len_tr = 4096*2;
+mu_ffe1 = 0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.004;
+mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+sps = 1;
+
+Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+%% -------------------- FFE --------------------
+% % requires some more digging what is going on :-)
+% eq_ffe = EQ("Ne",[200, 0, 0],"Nb",[0,0,0], ...
+% "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+% "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+% "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+%
+% ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+% "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+% "eth_style_symbol_mapping",mapping_style);
+%
+% % ffe_results.metrics.print
+% fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
+% BER_value = ffe_results.metrics.BER;
+
+%% -------------------- VNLE + MLSE --------------------
+
+pf_ncoeffs = 4;
+eq_v = EQ("Ne",[200, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','I2','weighted_DFE_I_mode',[5,0.5,0.6]);
+pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+mlse_results.metrics.print
+fprintf('My EQ: %.1e \n',mlse_results.metrics.BER);
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+
+%% -------------------- ML-based MLSE (L=2) --------------------
+% ml_mlse_equalizer = ML_MLSE("epochs_tr",400,"epochs_dd",1, ...
+% "len_tr",len_tr,"mu_dd",0.03,"mu_tr",0.03,"order",100,"sps",sps, ...
+% "traceback_depth",256,"L",2,"delta",4,"adaptive_mu",0);
+%
+% [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+% fprintf('ML-based MLSE:\n');
+% fprintf('My EQ: %.1e \n',ml_mlse_results.metrics.BER);
+% fprintf('Paper: %.1e \n \n',ber_in_paper);
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam_2.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam_2.m
new file mode 100644
index 0000000..d27d6e1
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf_pam_2.m
@@ -0,0 +1,272 @@
+%%
+clear all;
+close all;
+
+%%
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+% data_tr_mf is the already recovered and filtered data
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+%Calculate Transfer Function
+Tx_spectrum = Digi_sig_compare;
+Rx_spectrum = Scope_sig;
+
+[~,Rx_synced_spectrum,inverted_spectrum,sequenceFound_spectrum,sequenceStarts_spectrum] = Rx_spectrum.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_spectrum = Rx_synced_spectrum{1};
+
+Tx_spectrum = Tx_spectrum.resample("fs_out",fsym);
+Tx_spectrum.signal = fft(Tx_spectrum.signal);
+
+Rx_spectrum = Rx_spectrum.resample("fs_out",fsym);
+Rx_spectrum.signal = fft(Rx_spectrum.signal);
+
+H_transfer = Rx_spectrum.signal./Tx_spectrum.signal;
+H_inv = 1./H_transfer;
+
+%Number of Samples/Symbol after Matched Filter
+Kov = 14;
+
+Scope_sig = Scope_sig.resample('fs_in',fs,'fs_out',Kov*fsym);
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+% Rx_matched = Scope_sig;
+% Rx_matched = Rx_matched.resample('fs_out',Kov*fsym);
+
+data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+[~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+% timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+
+% Rx_matched = Scope_sig;
+% Rx_matched = Rx_matched.resample("fs_out",2*fsym);
+
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+Rx_matched_original = Rx_synced_cell{1};
+Rx_matched_original.signal = resample(Rx_matched_original.signal,1,Kov);
+Rx_matched_original.fs = fsym;
+
+% Rx_matched_1 = Rx_matched_1.resample("fs_in",Kov*fsym,'fs_out',2*fsym);
+
+Time_Rec = 1;
+if Time_Rec
+ % [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',Kov,'damping_factor',1,'normalized_loop_bandwidth',1e-4,'detector_gain',2.7).process(Rx_matched_1);
+ % Rx_matched_1.spectrum('normalizeTo0dB',1,"displayname",'Signal before Timing Recovery','fignum',1023);
+ % [Rx_Time_Rec, Timing_Error_MG] = Godard_Timing_Recovery('mode',3,'num_blocks',1,'fft_length',length(Rx_matched_1),'sps',Kov,'rolloff',0.6,'mu',-0.2,'Ki',1e-4).process(Rx_matched_1);
+ Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*16,'sps',Kov,'comp_signal',Rx_tr_mf,'comp_mode',0).process(Rx_matched_1);
+ % Rx_Time_Rec.spectrum('normalizeTo0dB',1,"displayname",'Signal after Timing Recovery','fignum',1023);
+ % idx = 1:2:length(Rx_Time_Rec);
+ % Rx_Time_Rec.signal = Rx_Time_Rec.signal(idx);
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal,1,Kov);
+ % Rx_Time_Rec = Rx_matched_1;
+ % Rx_Time_Rec.signal = resample(Rx_Time_Rec.signal,1,Kov);
+ % figure(2222222)
+ % plot(Timing_Error)
+ sps = 1;
+else
+ Rx_Time_Rec = Rx_matched_1;
+ sps = 1;
+end
+Rx_Time_Rec.fs = fsym;
+
+Rx_com = Rx_Time_Rec;
+Rx_com = Rx_com.resample('fs_in',fsym,'fs_out',2*fsym);
+Rx_com.spectrum("displayname",'After TR','normalizeTo0dB',1,'fignum',101114);
+Rx_matched_1.spectrum("displayname",'Before TR','normalizeTo0dB',1,'fignum',101114);
+Rx_tr_mf_2sps = Rx_tr_mf;
+Rx_tr_mf_2sps = Rx_tr_mf_2sps.resample('fs_in',fsym,'fs_out',2*fsym);
+Rx_tr_mf_2sps.spectrum("displayname",'Their signal after TR','normalizeTo0dB',1,'fignum',101114);
+
+% Rx_Time_Rec = Rx_Time_Rec.resample('fs_out',6e9);
+% Rx_tr_mf = Rx_tr_mf.resample('fs_out',fsym);
+
+% Amax = 10;
+% H_inv = min(abs(H_inv), Amax) .* exp(1j*angle(H_inv));
+% Rx_Time_Rec.signal = fft(Rx_Time_Rec.signal);
+% Rx_Time_Rec.signal = ifft(Rx_Time_Rec.signal .* H_inv);
+
+Rx_Time_Rec = Rx_Time_Rec.normalize('mode','rms');
+Rx_tr_mf = Rx_tr_mf.normalize('mode','rms');
+
+Rx_Time_Rec.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+% Rx_matched_original.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+Rx_tr_mf.spectrum('normalizeTo0dB',1,"displayname",'Their signal','fignum',101111);
+
+Rx_Time_Rec.normalize("mode","rms").plot("displayname",'Our signal','fignum',101311);
+% Rx_matched_original.normalize("mode","rms").plot("displayname",'Original signal','fignum',101311);
+Rx_tr_mf.normalize("mode","rms").plot("displayname",'Their signal','fignum',101311);
+
+%% not working..
+% Use our or their signal
+for our_signal = 1
+ if our_signal
+ Rx_synced = Rx_Time_Rec;
+ else
+ Rx_synced = Rx_tr_mf;
+ end
+ % Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = 4096*2;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[500, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % % eq_ffe = FFE_DFE('ffe_order',99,'dfe_order',99,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5,'sps',sps,'decide',0);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % if our_signal
+ % fprintf('Our signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = 4;
+ eq_v = EQ("Ne",[250, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','R2','weighted_DFE_I_mode',[5,0.5,0.6]);
+ % eq_v = FFE_DFE('ffe_order',200,'dfe_order',0,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5, ...
+ % 'ffe_mu_dd',mu_ffe,'ffe_mu_tr',0,'dfe_mu_dd',mu_dfe,'dfe_mu_tr',0.005,'sps',sps,'decide',0);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ if our_signal
+ fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ else
+ fprintf('Their Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ end
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",11,"sps",sps, ...
+ % "traceback_depth",256,"L",4,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % if our_signal
+ % fprintf('Our Signal: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/Evaluation Scripts/ml_mlse_complexity_plot.m b/projects/FSO_transmission/Evaluation Scripts/ml_mlse_complexity_plot.m
new file mode 100644
index 0000000..d30035b
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/ml_mlse_complexity_plot.m
@@ -0,0 +1,11 @@
+N = 1:4;
+S = 4;
+L = 1:4;
+Complexity_N = (N+1).*S.^(1+1);
+Complexity_L = (1+1).*S.^(L+1);
+
+figure;
+plot(N,Complexity_N)
+hold on
+plot(L,Complexity_L)
+hold off
diff --git a/projects/FSO_transmission/Evaluation Scripts/plotscript_baud_rate_sweep_method.m b/projects/FSO_transmission/Evaluation Scripts/plotscript_baud_rate_sweep_method.m
new file mode 100644
index 0000000..ae3a1b5
--- /dev/null
+++ b/projects/FSO_transmission/Evaluation Scripts/plotscript_baud_rate_sweep_method.m
@@ -0,0 +1,36 @@
+x = 4:8;
+for method = 1:4
+ filename = fullfile('C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\Silas DSP\imdd_simulation\projects\FSO_transmission\Data', ['BER_PAM_4_baud_rate_method' num2str(method) '.mat']);
+ data = load(filename);
+ BER = data.BER_PAM_4;
+ figure(22113344)
+ plot(x,BER,'-o','LineWidth',1.75);
+ hold on
+end
+
+h1 = yline(2e-2, ':k', 'LineWidth',1.5);
+h2 = yline(3.8e-3,':b', 'LineWidth',1.5);
+h3 = yline(4.85e-3,':g', 'LineWidth',1.5);
+h4 = yline(2.2e-4,':r', 'LineWidth',1.5);
+
+% Legende NUR für Kurven
+legend('FFE - 300 Taps','FFE+PF+MLSE - 300 FFE Taps - 4 PF Coefficients','ML-MLSE - 1000 epochs - 100th order - Memory Length = 2','DB - 300 FFE Taps',...
+ 'Interpreter','latex', ...
+ 'Location','southeast', 'FontSize', 14)
+
+% FEC Labels direkt im Plot
+text(6.85,2.3e-2,'o-FEC','Color','k','FontSize', 14, 'Interpreter','latex')
+text(7,3e-3,'HD-FEC','Color','b','FontSize', 14, 'Interpreter','latex')
+text(7,5.6e-3,'KP4+Hamming','Color','g','FontSize', 14,'Interpreter','latex')
+text(7,2.5e-4,'KP4','Color','r','FontSize', 14,'Interpreter','latex')
+
+xlabel('Symbol Rate [GBd]', 'Interpreter','latex')
+ylabel('BER', 'Interpreter','latex')
+% title('BER for PAM-4', 'Interpreter','latex')
+
+grid minor
+% ylim([1e-4 5e-2])
+xlim([3 9])
+set(gca,'YScale','log')
+hold off
+% beautifyBERplot
\ No newline at end of file
diff --git a/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m b/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m
new file mode 100644
index 0000000..36884ff
--- /dev/null
+++ b/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m
@@ -0,0 +1,251 @@
+%%
+clear all;
+close all;
+
+%% Choose a fitting base leading to the FSO Data
+base = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\";
+mode = 0; %0 oder 1
+M = 4;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+% data_tr_mf is the already recovered and filtered data
+if M == 2
+ tx_data_path = fullfile(base, "14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data_path = fullfile(base, "6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+ data_tr_mf = load("C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\FSO Equalizer\FSO_FP_QCL_60umUTC\Already Recovered and Filtered\AfterSync_M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+tx_data = load(tx_data_path);
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_compare = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+Scope_sig.plot("displayname",'raw','fignum',100);
+Scope_sig.spectrum("displayname",'raw','fignum',101)
+
+%Calculate Transfer Function
+Tx_spectrum = Digi_sig_compare;
+Rx_spectrum = Scope_sig;
+
+[~,Rx_synced_spectrum,inverted_spectrum,sequenceFound_spectrum,sequenceStarts_spectrum] = Rx_spectrum.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_spectrum = Rx_synced_spectrum{1};
+
+Tx_spectrum = Tx_spectrum.resample("fs_out",fsym);
+Tx_spectrum.signal = fft(Tx_spectrum.signal);
+
+Rx_spectrum = Rx_spectrum.resample("fs_out",fsym);
+Rx_spectrum.signal = fft(Rx_spectrum.signal);
+
+H_transfer = Rx_spectrum.signal./Tx_spectrum.signal;
+H_inv = 1./H_transfer;
+
+%Number of Samples/Symbol after Matched Filter
+Kov = 14;
+
+Scope_sig = Scope_sig.resample('fs_in',fs,'fs_out',Kov*fsym);
+
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef firectly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+Matched_Filter = Pulseformer("fsym",fsym,"fdac",Kov*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+Rx_matched = Matched_Filter.process(Scope_sig);
+Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1);
+
+% Loading and synchronizing their matched filtered and timing recovered data
+data_tr_mf = Electricalsignal(data_tr_mf.Results, "fs", fsym);
+[~,Rx_synced_cell_tr_mf,inverted_tr_mf,sequenceFound_tr_mf,sequenceStarts_tr_mf] = data_tr_mf.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_tr_mf = Rx_synced_cell_tr_mf{1};
+
+% Timing sync -> at this point we still have no symbol timing recovery, we
+% try to do this with 2sps EQ!
+[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
+Rx_matched_1 = Rx_synced_cell{1};
+
+% Timing recovery
+Time_Rec = 1;
+Timing_Mode = 3;
+if Time_Rec
+ if Timing_Mode == 1 % Zero-Crossing/Gardner/Early-Late/Müller-Mueller Timing Recovery
+ [Rx_Time_Rec, Timing_Error] = Timing_Recovery("modulation", 'PAM/PSK/QAM', "timing_error_detector",'Gardner (non-data-aided)','sps',Kov,'damping_factor',1,'normalized_loop_bandwidth',1e-4,'detector_gain',2.7).process(Rx_matched_1);
+ elseif Timing_Mode == 2 % Godard Timing Recovery (resampling is required as the Godard timing recovery does not change the number of samples per symbol)
+ [Rx_Time_Rec, Timing_Error_MG] = Godard_Timing_Recovery('mode',3,'num_blocks',1,'fft_length',length(Rx_matched_1),'sps',Kov,'rolloff',0.6,'mu',-0.2,'Ki',1e-4).process(Rx_matched_1);
+ Rx_Time_Rec = Rx_Time_Rec.resample('fs_in',Kov*fsym,'fs_out',fsym);
+ elseif Timing_Mode == 3 % Maximum Variance Timing Recovery
+ Rx_Time_Rec = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',Kov*fsym,'num_tau',Kov*128,'sps',Kov,'comp_signal',Rx_tr_mf,'comp_mode',0).process(Rx_matched_1);
+ end
+else
+ Rx_Time_Rec = Rx_matched_1.resample('fs_in',Kov*fsym,'fs_out',fsym);
+end
+sps = 1;
+Rx_Time_Rec.fs = fsym;
+
+% % Compensate using the inverse transfer function
+% Amax = 10;
+% H_inv = min(abs(H_inv), Amax) .* exp(1j*angle(H_inv));
+% Rx_Time_Rec.signal = fft(Rx_Time_Rec.signal);
+% Rx_Time_Rec.signal = ifft(Rx_Time_Rec.signal .* H_inv);
+
+% Normalization
+Rx_Time_Rec = Rx_Time_Rec.normalize('mode','rms');
+Rx_tr_mf = Rx_tr_mf.normalize('mode','rms');
+
+% Compare spectra of our and their time signal
+Rx_Time_Rec.spectrum("displayname",'Our signal','normalizeTo0dB',1,'fignum',101111);
+% Rx_matched_original.spectrum('normalizeTo0dB',1,"displayname",'Our signal','fignum',101111);
+Rx_tr_mf.spectrum("displayname",'Their signal','normalizeTo0dB',1,'fignum',101111);
+
+% Compare our and their time signal
+Rx_Time_Rec.normalize("mode","rms").plot("displayname",'Our signal','fignum',101311);
+% Rx_matched_original.normalize("mode","rms").plot("displayname",'Original signal','fignum',101311);
+Rx_tr_mf.normalize("mode","rms").plot("displayname",'Their signal','fignum',101311);
+
+%%
+% Use our or their signal
+for our_signal = 1
+ if our_signal
+ Rx_synced = Rx_Time_Rec;
+ else
+ Rx_synced = Rx_tr_mf;
+ end
+ % Rx_synced = Rx_Time_Rec;
+ % Rx_synced = Rx_synced_cell{1};
+ len_tr = 4096*2;
+ mu_ffe1 = 0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.004;
+ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
+ mu_dfe = 0.0004;
+ duob_mode = db_mode.no_db;
+
+ Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',103);
+ Rx_synced.spectrum("displayname",'RX: Matched+Sync+2sps','fignum',104);
+
+ Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',1);
+ Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',1);
+ Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',1);
+
+ if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+ elseif M == 4
+ ber_in_paper = 10^(-2.5);
+ end
+
+ %% -------------------- FFE --------------------
+ % requires some more digging what is going on :-)
+ % eq_ffe = EQ("Ne",[150, 0, 0],"Nb",[0,0,0], ...
+ % "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ % "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ % "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ %
+ % % eq_ffe = FFE_DFE('ffe_order',99,'dfe_order',99,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5,'sps',sps,'decide',0);
+ %
+ % ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ...
+ % "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
+ % "eth_style_symbol_mapping",mapping_style);
+ %
+ % if our_signal
+ % fprintf('Our signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their signal: %.1e \n',ffe_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+
+ %% -------------------- VNLE + MLSE --------------------
+ pf_ncoeffs = 4;
+ eq_v = EQ("Ne",[300, 0, 0],"Nb",[0, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1, ...
+ 'weighted_DFE',0,'weighted_DFE_d_min',0.5,'weighted_DFE_mode','R2','weighted_DFE_I_mode',[5,0.5,0.6]);
+ % eq_v = FFE_DFE('ffe_order',300,'dfe_order',5,'len_tr',len_tr,'epochs_tr',5,'epochs_dd',5, ...
+ % 'ffe_mu_dd',mu_ffe,'ffe_mu_tr',0,'dfe_mu_dd',mu_dfe,'dfe_mu_tr',0.005,'sps',sps,'decide',0);
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+
+ mlse_results.metrics.print
+ if our_signal
+ fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ else
+ fprintf('Their Signal: %.1e \n',mlse_results.metrics.BER);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ end
+
+ %% -------------------- ML-based MLSE (L=2) --------------------
+ % ml_mlse_equalizer = ML_MLSE("epochs_tr",100,"epochs_dd",1, ...
+ % "len_tr",length(Rx_synced),"mu_dd",0.03,"mu_tr",0.03,"order",11,"sps",sps, ...
+ % "traceback_depth",256,"L",4,"delta",4,"adaptive_mu",0);
+ %
+ % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style);
+ % if our_signal
+ % fprintf('Our Signal: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % else
+ % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER);
+ % fprintf('Paper: %.1e \n \n',ber_in_paper);
+ % end
+end
\ No newline at end of file
diff --git a/projects/FSO_transmission/first_analysis.m b/projects/FSO_transmission/first_analysis.m
new file mode 100644
index 0000000..9a327ab
--- /dev/null
+++ b/projects/FSO_transmission/first_analysis.m
@@ -0,0 +1,252 @@
+
+base = "C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC";
+mode = 0; %0 oder 1
+M = 2;
+
+all_files = dir(fullfile(base, "**/*.mat"));
+
+if M == 2
+ tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
+ filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
+elseif M == 4
+ tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
+ filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
+end
+
+if mode == 1
+ [f, p] = uigetfile(fullfile(base, "**/*.mat"));
+ if f~=0
+ filename = fullfile(p,f);
+ end
+end
+
+
+datas = load(filename);
+
+%%
+str = filename;
+M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once'));
+assert(M==M_);
+fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once'));
+fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once'));
+I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f');
+rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f');
+L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f');
+pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once'));
+rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once'));
+mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once'));
+
+%%
+% Tx data
+
+Bits = Informationsignal(tx_data.tx_data,"fs",fsym);
+Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym);
+
+mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there
+PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme
+
+Symbols_ = PM.map(Bits) .* PM.scaling;
+assert(isequal(Symbols.signal,Symbols_.signal));
+
+Bits_ = PM.demap(Symbols);
+[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal);
+assert(ber == 0);
+
+%% For comparison, apply pulsef on Tx Symbols
+Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sig_tx_compare = Pform.process(Symbols);
+
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Digi_sync = Pform.process(Symbols);
+
+MF = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
+Rx_sig_compare = MF.process(Digi_sig_tx_compare);
+
+%%
+
+% Rx Data
+traceData = datas.tr.lastData(2).trace.ch3;
+
+%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin
+scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin;
+demystified = isequal(traceData.YData,scoperead_volts);
+assert(demystified);
+
+timesig_compare = [0:1:datas.tr.lastData(1).trace.ch3.Points-1] ./ fs;
+timesig = datas.tr.lastData(1).trace.ch3.XData;
+assert(isequal(traceData.YData,scoperead_volts));
+
+Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
+
+%%
+% 1) matched filter
+% pulse is symmetric, hence we can use pulsef directly as matched filter.
+% It feels off (bit I think correct) that the fsym is now the output freq.!!
+% -> output 2 sps to omit timing recovery!?
+apply_matched_filter = 1;
+k = 1;
+if apply_matched_filter
+
+ Pform = Pulseformer("fsym",fsym,"fdac",k*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
+ Rx_matched = Pform.process(Scope_sig);
+else
+
+ Rx_matched = Filter('filtdegree',4,"f_cutoff",fsym*0.5,"fs",Scope_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scope_sig);
+ Rx_matched = Rx_matched.resample("fs_out",k*fsym);
+end
+Rx_matched.spectrum();
+
+
+%%
+
+coefficients = arburg(Rx_matched.signal,25);
+
+figure()
+[h,w] = freqz(1,coefficients,Rx_matched.length,"whole",Rx_matched.fs);
+h = h/max(abs(h));
+hold on
+w_ = (w - Rx_matched.fs/2);
+plot(w_.*1e-9,20*log10(fftshift(abs(h))),'DisplayName',['Burg Coeffs: ', num2str(round(coefficients,2)), ' '],'LineWidth',2);
+
+%% Timing Rec
+apply_timing_rec = 1;
+if apply_timing_rec
+ [Rx_symbolsync, timing_error] = Timing_Recovery("timing_error_detector",'Gardner (non-data-aided)','sps',k,'damping_factor',0.1,'normalized_loop_bandwidth',0.1,'detector_gain',2.7).process(Rx_matched);
+ figure();plot(timing_error);
+ Rx_symbolsync.fs = fsym;
+ % Tsynch
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
+ Rx_synced = Rx_synced_cell{1};
+ sps = 1;
+else
+ % Tsynch
+ [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
+ Rx_synced = Rx_synced_cell{1};
+ Rx_synced = Rx_synced.resample("fs_out",2*fsym);
+ sps = 2;
+end
+
+if M == 2
+ ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
+elseif M == 4
+ ber_in_paper = 10^(-2.5);
+end
+
+
+%%
+
+Rx_synced = Rx_synced_cell{1};
+
+
+% -------------------- FFE --------------------
+% requires some more digging what is going on :-)
+eq_ffe = EQ("Ne",[50, 1, 1],"Nb",[2,0,0], ...
+ "training_length",512,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+vars = logspace(-4,-3,36);
+
+parfor i = 1:numel(vars)
+
+ len_tr = 4096;
+ mu_ffe1 = 0.01;% mus(i);%0.0001;
+ mu_ffe2 = 0.0008;
+ mu_ffe3 = 0.001;
+ mu_dc = 0.005;
+ mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
+ mu_dfe = vars(i);
+ duob_mode = db_mode.no_db;
+
+ % requires some more digging what is going on :-)
+ eq_ffe_1 = EQ("Ne",[150, 1, 0],"Nb",[50,0,0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",0);
+
+ eq_ffe_2 = FFE("epochs_tr",1,"epochs_dd",vars(i),"len_tr",4096,"mu_dd",vars(i),"mu_tr",vars(i),"order",999,"sps",1,"decide",0, "adaption",adaption_method.nlms,"dd_mode",0);
+ % eq_ffe_2 = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-5,"dfe_mu_dd",mus(i),"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",50,"dfe_order",10,"sps",1,"decide",1);
+
+
+ ffe_results = ffe(eq_ffe_1,M,Rx_synced,Symbols,Bits, ...
+ "precode_mode",duob_mode,'showAnalysis',1,"postFFE",[], ...
+ "eth_style_symbol_mapping",mapping_style);
+
+ ffe_results.metrics.BER
+ bers(i) = ffe_results.metrics.BER;
+end
+
+figure();
+plot(vars,bers);
+yline(ber_in_paper)
+beautifyBERplot();
+%
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+ffe_results.metrics.print("description",'FFE');
+fprintf('FFE: %.1e \n',ffe_results.metrics.BER);
+
+
+%% -------------------- VNLE + MLSE --------------------
+len_tr = 4096;
+mu_ffe1 = 0.0001;% mus(i);%0.0001;
+mu_ffe2 = 0.0008;
+mu_ffe3 = 0.001;
+mu_dc = 0.005;
+mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
+mu_dfe = 0.0004;
+duob_mode = db_mode.no_db;
+
+pf_ncoeffs = 4;
+
+vars = 1:7;
+bers = zeros(size(vars));
+parfor i = 1:numel(vars)
+
+ eqv = EQ("Ne",[200, 1, 0],"Nb",[2, 0, 0], ...
+ "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
+ "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+ pf_ncoeffs = vars(i);
+ pf = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
+ [vnle_results, mlse_results] = vnle_postfilter_mlse(eqv, pf, mlse_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
+ fprintf('Paper: %.1e \n \n',ber_in_paper);
+ vnle_results.metrics.print("description",'VNLE');
+ mlse_results.metrics.print("description",'MLSE');
+ bers(i) = mlse_results.metrics.BER;
+end
+%%
+figure();hold on
+plot(vars,bers_ffe,'DisplayName','FFE [200,0,0] + PF + MLSE');
+plot(vars,bers_vnle,'DisplayName','VNLE [200,1,0] + PF + MLSE');
+plot(vars,bers_vnledfe,'DisplayName','VNLE [200,1,0] + DFE [2] + PF + MLSE');
+plot(vars,bers_vnledfe_ideal,'DisplayName','VNLE [200,1,0] + ideal DFE [2] + PF + MLSE');
+yline(ber_in_paper);
+yline([2e-2, 4.85e-3, 3.8e-3, 2,2e-4],'LineWidth',2,'Color',[0.8,0.8,0.8],'LineStyle',':','HandleVisibility','off');
+ylim([1e-5,0.1]);
+beautifyBERplot();
+
+%% -------------------- DB target --------------------
+mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
+
+eq_ = EQ("Ne",[50, 5, 5],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
+
+dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
+ "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
+
+fprintf('Paper: %.1e \n \n',ber_in_paper);
+dbt_results.metrics.print("description",'Duobinary');
+
+
+%%
+
+%ML-based MLSE (L=2)
+mu_ml = 0.01; training_epochs = 100;
+ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
+ "len_tr",len_tr,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",1, ...
+ "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0);
+
+[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode);
+ml_mlse_results.metrics.print("description",'ML ');
\ No newline at end of file
diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m
index 21f881f..986fcdb 100644
--- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m
+++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m
@@ -6,7 +6,7 @@ db = DBHandler("dataBase", "labor_highspeed", "type", database_type);
pam_levels = [4, 6, 8]; % three tiles
bitrate_set = 360e9;
-fiberL = 10;
+fiberL = 2;
fields = [
db.getTableFieldNames('power_state_info');
@@ -96,7 +96,7 @@ end
%% ============================================================
% PLOT — 1×3 (PAM-4, PAM-6, PAM-8)
% ============================================================
-fig = figure(9110); clf;
+fig = figure(9112); clf;
tiledlayout(1,3,'TileSpacing','compact','Padding','compact');
lw = 1.8;
@@ -223,19 +223,19 @@ ylabel('');
end
-pos = 1e3.*[2.7770 1.2017 1.4000 0.3200];
-set(fig, 'Position', pos);
+% pos = 1e3.*[2.7770 1.2017 1.4000 0.3200];
+% set(fig, 'Position', pos);
%% === EXPORT ===
-outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\wavelength_analysis.tikz';
-matlab2tikz(outfile, ...
- 'width','\fwidth', ...
- 'height','\fheight', ...
- 'showInfo',false, ...
- 'extraAxisOptions',{ ...
- 'legend style={font=\footnotesize}', ...
- 'legend columns=1' ...
- });
+% outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\wavelength_analysis.tikz';
+% matlab2tikz(outfile, ...
+% 'width','\fwidth', ...
+% 'height','\fheight', ...
+% 'showInfo',false, ...
+% 'extraAxisOptions',{ ...
+% 'legend style={font=\footnotesize}', ...
+% 'legend columns=1' ...
+% });
diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m
index 2213c8a..147c7d9 100644
--- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m
+++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m
@@ -1,83 +1,139 @@
+tablename = 'C:\Users\Silas\Documents\latex\JLT_400G_submission\HighSpeedExperiments_oneandonly_csv.csv';
+% Returns a Table
+data = readtable(tablename,"Delimiter",';','DecimalSeparator',',');
+
+
%% ============================================================
% PLOT
% ============================================================
+%% 1. DATA EXTRACTION & SETUP
+%% 1. DATA EXTRACTION & SETUP
+raw_M = data.M;
+raw_baud = data.BaudRate;
+raw_net = data.NetRate;
+raw_codes = string(data.ZoteroCode);
+raw_names = string(data.Name);
+raw_band = string(data.Band);
+
+% Filter Valid Data
+target_M = [2, 4, 6, 8];
+validIdx = ismember(raw_M, target_M) & ~isnan(raw_baud) & ~isnan(raw_net);
+
+Mvals = raw_M(validIdx);
+baud = raw_baud(validIdx);
+netrate = raw_net(validIdx);
+codes = raw_codes(validIdx);
+names = raw_names(validIdx);
+bands = raw_band(validIdx);
+
+pam_list = target_M;
+colors = flip(cbrewer2('SET1',4));
+
+%% 2. PLOT (For Visual Check only)
figure; hold on;
-ms = 32; % scatter size
-lw = 0.8; % line width
-
-for k = 1:4 % PAM-2/4/6/8
+ms = 20;
+lw = 0.5;
+for k = 1:length(pam_list)
M = pam_list(k);
idxPam = (Mvals == M);
-
- % Extract for this PAM
+
x = baud(idxPam);
y = netrate(idxPam);
+ b = bands(idxPam);
n = names(idxPam);
-
- % Get color for this PAM format
col = colors(k,:);
-
- % ----- LEGEND FLAG (only add one entry per PAM) -----
+
firstLegend = true;
-
- % ---- PLOT ALL POINTS (marker based on publication) ----
+
for i = 1:sum(idxPam)
- % marker selection by publication
- pubIdx = find(pub_list == n(i), 1);
- marker = markerlist{mod(pubIdx-1, nMarkers) + 1};
-
+ % Marker Logic
+ ms = 20;
+ if strcmpi(b(i), 'O')
+ marker = 'o';
+ elseif strcmpi(b(i), 'C')
+ marker = 'd';
+ else
+ marker = 's';
+ end
+ if strcmpi(n(i), 'THIS WORK')
+ marker = 'pentagram';
+ ms = 100;
+ end
+
+ % Plot Scatter
if firstLegend
- h = scatter(x(i), y(i), ms, ...
- 'Marker', marker, ...
- 'MarkerEdgeColor', col, ...
- 'MarkerFaceColor', col, ...
+ scatter(x(i), y(i), ms, 'Marker', marker, ...
+ 'MarkerEdgeColor', col, 'MarkerFaceColor', col, ...
'DisplayName', sprintf('PAM-%d', M));
firstLegend = false;
else
- h = scatter(x(i), y(i), ms, ...
- 'Marker', marker, ...
- 'MarkerEdgeColor', col, ...
- 'MarkerFaceColor', col, ...
+ scatter(x(i), y(i), ms, 'Marker', marker, ...
+ 'MarkerEdgeColor', col, 'MarkerFaceColor', col, ...
'HandleVisibility','off');
end
-
- % ====== CUSTOM DATATIP CONTENT ======
- dt = h.DataTipTemplate;
- dt.DataTipRows(1).Label = 'Baud rate';
- dt.DataTipRows(2).Label = 'Net rate';
-
- % Add publication name
- dt.DataTipRows(end+1) = dataTipTextRow('Publication', n(i));
-
-
end
-
- % ---- Fit (PAM-specific) ----
- valid = ~isnan(x) & ~isnan(y);
- if sum(valid) >= 3
- p = polyfit(x(valid), y(valid), 2);
- xfit = linspace(min(x(valid)), max(x(valid)), 200);
- yfit = polyval(p, xfit);
-
- plot(xfit, yfit, ':', ...
- 'LineWidth', lw, ...
- 'Color', col, ...
- 'HandleVisibility', 'off'); % do NOT add to legend
+
+ % Fit lines
+ if length(x) >= 3
+ [p, S, mu] = polyfit(x, y, 2);
+ xfit = linspace(min(x), max(x), 200);
+ yfit = polyval(p, xfit, S, mu);
+ plot(xfit, yfit, '-', 'LineWidth', lw, 'Color', col, 'HandleVisibility', 'off');
end
end
-grid on;
+grid on; box on;
xlabel('Baud rate [GBd]');
ylabel('Net rate [Gb/s]');
-
-legend('Location','northwest');
-set(gca,'FontSize',11);
-
+% title('Check Command Window for TikZ Code');
+% legend('Location','northwest');
+
+%% 3. GENERATE TIKZ ANNOTATION CODE
+% This prints the manual \draw commands to the console
+
+%% GENERATE TIKZ ANNOTATION CODE
+% This prints the manual \draw commands to the console
+
+%% GENERATE TIKZ ANNOTATION CODE (Colored Borders + Tiny Font)
+%% GENERATE TIKZ ANNOTATION CODE (No Arrow, Close Text)
+fprintf('\n\n%% ===========================================================\n');
+fprintf('%% COPY THE FOLLOWING LINES INTO YOUR .TEX FILE \n');
+fprintf('%% (Paste them just before \\end{axis})\n');
+fprintf('%% ===========================================================\n\n');
+
+for i = 1:length(baud)
+ bx = baud(i);
+ by = netrate(i);
+ key = codes(i);
+ M_val = Mvals(i);
+
+ % --- PLACEMENT LOGIC ---
+ if M_val == 8
+ % PAM-8: Place Top-Left
+ % 'south east' anchor means the text's bottom-right corner touches the coordinate
+ % shift moves it slightly up and left to clear the marker
+ anchorStr = 'south east';
+ shiftStr = 'shift={(-3pt, 3pt)}';
+ else
+ % Others: Place Bottom-Right
+ % 'north west' anchor means the text's top-left corner touches the coordinate
+ % shift moves it slightly down and right
+ anchorStr = 'north west';
+ shiftStr = 'shift={(3pt, -3pt)}';
+ end
+
+ % --- PRINT COMMAND ---
+ % Uses \node directly at the coordinate (axis cs:...)
+ fprintf('\\node[anchor=%s, %s, font=\\tiny, fill=white, inner sep=1pt] at (axis cs:%.2f, %.2f) {\\cite{%s}};\n', ...
+ anchorStr, shiftStr, bx, by, key);
+end
+fprintf('\n')
%% === EXPORT ===
-outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\highspeedresults.tikz';
+outfile = 'C:\Users\Silas\Documents\latex\JLT_400G_submission\media\matlab2tikz\highspeedresults_test.tikz';
+
matlab2tikz(outfile, ...
'width','\fwidth', ...
'height','\fheight', ...
diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m
index ab31451..67a2ff8 100644
--- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m
+++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m
@@ -10,10 +10,11 @@ if dsp_options.mode == "load_run_id"
if experiment == "highspeed_2024"
- dsp_options.database_type = 'mysql';
+ dsp_options.database_type = "mysql";
dsp_options.dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
- db = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type);
+ db = DBHandler("dataBase", [dsp_options.dataBase],...
+ "type", dsp_options.database_type,"server","192.168.178.192","user","silas","password","silas");
elseif experiment == "mpi_ecoc_2025"
diff --git a/projects/ML_based_MLSE/analyze_filter_length.m b/projects/ML_based_MLSE/analyze_filter_length.m
index 616b715..1b31d6b 100644
--- a/projects/ML_based_MLSE/analyze_filter_length.m
+++ b/projects/ML_based_MLSE/analyze_filter_length.m
@@ -5,9 +5,9 @@ M = 4;
randkey = 1;
% --- Parameter sweep
-order_range = 2:3:11; % FFE order
-delta_range = 0:2:4; % delta
-SNR_dB = 20;
+order_range = 5:5:50; % FFE order
+delta_range = 0:5:20; % delta
+SNR_dB = 30;
% --- Prepare bit sequence
order_bits = 19;
@@ -21,10 +21,14 @@ Symbols = PAMmapper(M,0).map(Bits);
Symbols.fs = 200e9;
% --- Channel (minimal ISI + AWGN)
-h = [0.3 0.9 0.3]; h = h/norm(h);
+h = abs([0.3 0.9 0.3]); h = h/norm(h);
+
+% h = [1 -1.67085330039878 1.17918163282514 -0.805210559745616 0.571564213123367 -0.296337147529674 0.00649773445209780 0.0854177610195952 -0.0576009020965258 0.0520994427061551 -0.0624586034913656 0.0553280962699552 -0.00705582559925755 -0.0336399056707792 0.0706903719452810 -0.0334124287931977 0.0131699455037966 0.0587431373842994 -0.0515902976066452 0.00647904355473619 0.0137506750904990 -0.0547974515885928 0.00994735499340592 -0.0135513582534086 -0.00463322575007739 0.0277311946101940];
+% h = h/norm(h);
symbols_filt = Symbols.filter(h,1);
symbols_noi = symbols_filt;
symbols_noi.signal = awgn(symbols_filt.signal,SNR_dB,'measured');
+symbols_noi.spectrum();
% --- Generate all parameter pairs
[O,D] = ndgrid(order_range, delta_range);
@@ -38,7 +42,7 @@ ce_vec = nan(size(pairs,1),1);
ce_training = nan(size(pairs,1),training_len);
% --- Parallel loop over parameter pairs
-parfor k = 1:size(pairs,1)
+for k = 1:size(pairs,1)
order_k = pairs(k,1);
delta_k = pairs(k,2);
@@ -89,7 +93,7 @@ end
beautifyBERplot
ylabel('BER'); xlabel('Filter Order [N]');
title('BER vs. Filter order');
-ylim([1e-4, 0.1]);
+% ylim([1e-4, 0.1]);
yline(3.8e-3,'HandleVisibility','off');
yline(2.2e-4,'HandleVisibility','off');
diff --git a/projects/ML_based_MLSE/theoretic_channel_evaluation.m b/projects/ML_based_MLSE/theoretic_channel_evaluation.m
index 3e1efcc..3eb6f09 100644
--- a/projects/ML_based_MLSE/theoretic_channel_evaluation.m
+++ b/projects/ML_based_MLSE/theoretic_channel_evaluation.m
@@ -48,36 +48,36 @@ for i = 1:numel(SNR_dB)
symbols_noi = symbols_filt;
symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB(i), 'measured'); % AWGN with given SNR
- % % Sequence Est L=5
- % mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',h);
- % mlse_.DIR = h;
- % [y_mlse] = mlse_.process(symbols_noi,Symbols);
- % mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
- % [~, ~, ber_mlse_l5(i), ~] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
- % fprintf('MLSE L5: %.2e \n',ber_mlse_l5(i));
- %
- % % 2nd Approach
- % mu_lms = 0.0005;
- % pf_ncoeffs = 1;
- % eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",16,"sps",1,"dd_mode",1,"adaption_technique","lms");
- % pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
- %
- % % FFE
- % [y_ffe, ffe_noise] = eq_.process(symbols_noi, Symbols);
- %
- % Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ffe);
- % [~, ~, ber_ffe(i), ~] = calc_ber(Eq_bits.signal, Bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
- % fprintf('FFE: %.2e \n',ber_ffe(i));
- %
- % % Postfilter
- % [y_white,~] = pf_.process(y_ffe, ffe_noise);
- %
- % % Sequence Est
- % mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
- % [y_mlse] = mlse_.process(y_white,Symbols);
- % mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
- % [~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
- % fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i));
+ % Sequence Est L=5
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',h);
+ mlse_.DIR = h;
+ [y_mlse] = mlse_.process(symbols_noi,Symbols);
+ mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
+ [~, ~, ber_mlse_l5(i), ~] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
+ fprintf('MLSE L5: %.2e \n',ber_mlse_l5(i));
+
+ % 2nd Approach
+ mu_lms = 0.0005;
+ pf_ncoeffs = 1;
+ eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",16,"sps",1,"dd_mode",1,"adaption_technique","lms");
+ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
+
+ % FFE
+ [y_ffe, ffe_noise] = eq_.process(symbols_noi, Symbols);
+
+ Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ffe);
+ [~, ~, ber_ffe(i), ~] = calc_ber(Eq_bits.signal, Bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
+ fprintf('FFE: %.2e \n',ber_ffe(i));
+
+ % Postfilter
+ [y_white,~] = pf_.process(y_ffe, ffe_noise);
+
+ % Sequence Est
+ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
+ [y_mlse] = mlse_.process(y_white,Symbols);
+ mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
+ [~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
+ fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i));
% ML-base MLSE L=2
adaptive_mu = 0;
diff --git "a/projects/Messung_Z\303\274rich/minimal_matched.m" "b/projects/Messung_Z\303\274rich/minimal_matched.m"
new file mode 100644
index 0000000..a5dfa33
--- /dev/null
+++ "b/projects/Messung_Z\303\274rich/minimal_matched.m"
@@ -0,0 +1,60 @@
+L = 4; % Oversampling factor
+fsym = 1e9/4;
+rollOff = 0.5; % Pulse shaping roll-off factor
+htx = rcosdesign(rollOff,16, L, 'sqrt');
+hrx = conj(fliplr(htx));
+
+signal = zeros(1000,1);
+signal(500) = 1;
+Digi_sig = Informationsignal(signal,"fs",fsym);
+
+% Digi_sig = Digi_sig.resample("fs_out",L*fsym);
+%
+% Digi_sig_tx = Digi_sig.filter(htx,1);
+% Digi_sig_rx = Digi_sig_tx.filter(hrx,1);
+%
+% figure;plot(Digi_sig_tx.signal);hold on;plot(Digi_sig_rx.signal),plot(Digi_sig.signal);
+%
+%
+%
+%
+pulsef = Pulseformer("alpha",1,"matched",0,"fdac",Digi_sig.fs*L,"fsym",fsym,"pulse","rrc","pulselength",16);
+Digi_sig = pulsef.process(Digi_sig);
+
+pulsef = Pulseformer("alpha",1,"matched",1,"fdac",Digi_sig.fs,"fsym",fsym,"pulse","rrc","pulselength",16);
+Digi_sig_matched = pulsef.process(Digi_sig);
+
+% Filter:
+htx = rcosdesign(rollOff,16, L, 'sqrt');
+% Note half of the target delay is used, because when combined
+% to the matched filter, the total delay will be achieved.
+hrx = conj(fliplr(htx));
+
+figure
+plot(htx)
+title('Transmit Filter')
+xlabel('Index')
+ylabel('Amplitude')
+
+figure
+plot(hrx)
+title('Rx Filter (Matched Filter)')
+xlabel('Index')
+ylabel('Amplitude')
+
+p = conv(htx,hrx);
+
+figure
+plot(p)
+title('Combined Tx-Rx = Raised Cosine')
+xlabel('Index')
+ylabel('Amplitude')
+
+% And let's highlight the zero-crossings
+zeroCrossings = NaN*ones(size(p));
+zeroCrossings(1:L:end) = 0;
+zeroCrossings((rcDelay)*L + 1) = NaN; % Except for the central index
+hold on
+plot(zeroCrossings, 'o')
+legend('RC Pulse', 'Zero Crossings')
+hold off
\ No newline at end of file
diff --git a/projects/WDM/WDM_model_10km_queue.m b/projects/WDM/WDM_model_10km_queue.m
index a41cb02..8aae2ea 100644
--- a/projects/WDM/WDM_model_10km_queue.m
+++ b/projects/WDM/WDM_model_10km_queue.m
@@ -312,7 +312,7 @@ for realiz = 1:s.num_realiz
Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
- "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1).process(Opt_sig_wdm_fib);
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",1,"useSingle",1).process(Opt_sig_wdm_fib);
% -------- Evaluate at intermediate distance (enqueue jobs) --------
if eval_ptr <= nEval && seg == eval_seg(eval_ptr)
diff --git a/test/bayesopt_ffe_tuning.m b/test/bayesopt_ffe_tuning.m
new file mode 100644
index 0000000..adedb3d
--- /dev/null
+++ b/test/bayesopt_ffe_tuning.m
@@ -0,0 +1,219 @@
+%% Bayesian Optimization for FFE Parameter Tuning
+% This script uses bayesopt to find optimal mu_dd and mu_tr values
+% that minimize BER for the FFE equalizer.
+
+clear; clc;
+
+%% Setup - Same as gpu_processing_dpfiber.m
+s.wavelengthplan = calcWavelengthPlan(4, 400e9, 1310);
+link_length = 10;
+s.pmd = 0.1;
+s.gamma = 0.0023;
+
+s.M = 4;
+fsym = 112e9;
+fdac = 2*fsym;
+fadc = 120000000000;
+s.random_key = 1;
+
+% Laser / Modulator
+vbias_rel = 0.5;
+u_pi = 4.6;
+vbias = -vbias_rel*u_pi;
+laser_linewidth = 0e6;
+
+duob_mode = db_mode.no_db;
+rcalpha = 0.05;
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
+
+s.chirpalpha = 0;
+s.p_launch = 3;
+s.p = "co";
+
+N = numel(s.wavelengthplan);
+
+switch s.p
+ case "co"
+ pol_rot = 100.*ones(1,N);
+ d_local = 0;
+end
+
+f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
+margin = 25e12;
+f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
+f_nyq = f_span/2;
+
+kover = 4;
+upsample_required = f_nyq./(fdac*kover/2);
+upsample_pow = 2^nextpow2(upsample_required);
+
+s.f_opt = fdac*kover*upsample_pow;
+s.f_opt_nyq = s.f_opt/2;
+
+s.rop = -8; % Fixed ROP for optimization
+
+%% Generate TX signals (run once)
+fprintf('Generating TX signals...\n');
+for l = 1:N
+ [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
+ "fsym",fsym,"M",s.M,"order",15,"useprbs",0, ...
+ "fs_out",fdac, ...
+ "applyclipping",0,"clipfactor",1.5, ...
+ "applypulseform",1,"pulseformer",Pform, ...
+ "randkey",s.random_key+l, ...
+ "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
+ ).process();
+
+ Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
+ "filterType",filtertypes.gaussian,"active",true);
+
+ El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
+ "bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
+ "H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
+
+ El_sig = El_sig.normalize("mode","oneone");
+ scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
+ El_sig = El_sig .* scaling;
+
+ Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
+ "lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
+ "linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
+
+ signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
+end
+
+%% WDM mux + launch
+Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
+ "lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
+
+Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
+
+%% Fiber propagation
+segment_length = 1;
+nSegments = link_length/segment_length;
+nSegments = round(nSegments);
+
+zdw = 1310;
+randomize_D = true;
+Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
+
+Opt_sig_wdm_fib = Opt_sig_wdm;
+fprintf('Running fiber propagation...\n');
+for seg = 1:nSegments
+ fprintf('Segment %d/%d\n', seg, nSegments);
+ Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",true).process(Opt_sig_wdm_fib);
+end
+
+%% Pre-process to get Rx_sig (do demux once)
+fprintf('Pre-processing receiver chain...\n');
+l = 1; % Use channel 1 for optimization
+
+Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
+ "fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_wdm_fib);
+
+Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",s.rop).process(Opt_sig_demux{l});
+
+PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
+ "nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
+
+rx_bwl = 100e9;
+PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
+ "filterType",filtertypes.butterworth,"active",true).process(PD_sig);
+
+Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
+Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
+ "delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
+ "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
+ "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
+
+Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
+
+[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
+Rx_sig = Scpe_cell{1};
+Rx_sig = Rx_sig.normalize("mode","rms");
+
+fprintf('Receiver pre-processing complete. Ready for optimization.\n\n');
+
+%% Define the objective function for bayesopt
+function ber = ffe_objective(params, Rx_sig, Symbols_l, Tx_bits_l, M, duob_mode)
+mu_dd = params.mu_dd;
+mu_tr = params.mu_tr;
+
+try
+ eq_ffe = FFE("epochs_tr", 5, "epochs_dd", 2, "len_tr", 2^13, ...
+ "mu_dd", mu_dd, "mu_tr", mu_tr, ...
+ "order", 50, "sps", 2, "decide", 0, ...
+ "adaption", adaption_method.nlms, "dd_mode", 1);
+
+ ffe_results = ffe(eq_ffe, M, Rx_sig, Symbols_l, Tx_bits_l, ...
+ "precode_mode", duob_mode, ...
+ 'showAnalysis', 0, ...
+ "postFFE", [], ...
+ "eth_style_symbol_mapping", 0);
+
+ ber = ffe_results.metrics.BER;
+
+ if ber == 0
+ ber = 1e-10;
+ end
+
+ if ~isfinite(ber)
+ ber = 0.5;
+ end
+
+ fprintf(' mu_dd=%.4e, mu_tr=%.4e -> BER=%.4e\n', mu_dd, mu_tr, ber);
+
+catch ME
+ fprintf(' mu_dd=%.4e, mu_tr=%.4e -> FAILED (%s)\n', mu_dd, mu_tr, ME.message);
+ ber = 0.5;
+end
+end
+
+%% Define optimizable variables
+mu_dd_var = optimizableVariable('mu_dd', [1e-5, 0.1], 'Transform', 'log');
+mu_tr_var = optimizableVariable('mu_tr', [1e-5, 0.1], 'Transform', 'log');
+
+%% Run Bayesian Optimization
+fprintf('========== Starting Bayesian Optimization ==========\n');
+fprintf('Optimizing mu_dd and mu_tr to minimize BER\n');
+fprintf('Search range: mu_dd=[1e-5, 0.1], mu_tr=[1e-5, 0.1]\n\n');
+
+objective_fn = @(params) ffe_objective(params, Rx_sig, Symbols{l}, Tx_bits{l}, s.M, duob_mode);
+
+results = bayesopt(objective_fn, [mu_dd_var, mu_tr_var], ...
+ 'MaxObjectiveEvaluations', 30, ...
+ 'AcquisitionFunctionName', 'expected-improvement-plus', ...
+ 'IsObjectiveDeterministic', false, ...
+ 'ExplorationRatio', 0.5, ...
+ 'Verbose', 1, ...
+ 'PlotFcn', []);
+
+%% Display Results
+fprintf('\n========== FFE Optimization Complete ==========\n');
+fprintf('Best FFE parameters found:\n');
+fprintf(' mu_dd = %.6e\n', results.XAtMinObjective.mu_dd);
+fprintf(' mu_tr = %.6e\n', results.XAtMinObjective.mu_tr);
+fprintf(' BER = %.6e\n', results.MinObjective);
+
+%% Verify with optimal parameters
+fprintf('\nVerifying optimal FFE parameters...\n');
+best_mu_dd = results.XAtMinObjective.mu_dd;
+best_mu_tr = results.XAtMinObjective.mu_tr;
+
+eq_ffe_best = FFE("epochs_tr", 5, "epochs_dd", 2, "len_tr", 2^13, ...
+ "mu_dd", best_mu_dd, "mu_tr", best_mu_tr, ...
+ "order", 50, "sps", 2, "decide", 0, ...
+ "adaption", adaption_method.nlms, "dd_mode", 1);
+
+ffe_results_best = ffe(eq_ffe_best, s.M, Rx_sig, Symbols{l}, Tx_bits{l}, ...
+ "precode_mode", duob_mode, ...
+ 'showAnalysis', 1, ...
+ "postFFE", [], ...
+ "eth_style_symbol_mapping", 0);
+
+fprintf('\nFinal FFE BER with optimal parameters: %.6e\n', ffe_results_best.metrics.BER);
diff --git a/test/bitwise_demapping_pam6.m b/test/bitwise_demapping_pam6.m
new file mode 100644
index 0000000..6ea0b3d
--- /dev/null
+++ b/test/bitwise_demapping_pam6.m
@@ -0,0 +1,43 @@
+
+
+%%%%% SETTINGS %%%%%%
+useprbs = 1;
+M = 8;
+randkey = 1;
+fsym = 112e9;
+viewresults = 0;
+
+%%%%% Mapping %%%%%
+M = 6;
+data = 0:M-1;
+bitpersymbol = log2(M);
+
+s = RandStream('twister','Seed',1);
+bitpattern = randi(s,[0 1], 2^18, 1);
+bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
+
+bits_tx = Informationsignal(bitpattern);
+
+symbols = PAMmapper(M,0).map(bits);
+
+pam6transitions = combvec(PAMmapper(M,0).levels,PAMmapper(M,0).levels)';
+
+pam6bits = PAMmapper(6,0,"eth_style",0).demap(reshape(pam6transitions',[],1)./sqrt(10));
+symbols_rx = PAMmapper(M,0).map(pam6bits).*sqrt(10);
+
+pam6bits = reshape(pam6bits',5,[])';
+
+figure; hold on
+scatter(pam6transitions(:,1), pam6transitions(:,2), 'x', 'LineWidth', 1);
+n = size(pam6transitions,1);
+labels = cellstr(char(pam6bits + '0')); % -> N x 1 cell array of char rows
+text(pam6transitions(:,1), pam6transitions(:,2), labels, ...
+ 'HorizontalAlignment','left', 'VerticalAlignment','bottom');
+
+
+
+bits_rx = PAMmapper(M,0).demap(symbols);
+
+[~,error_num,ber,error_pos] = calc_ber(bits_tx.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
+
+PAMmapper(8,0).showBitMapping
\ No newline at end of file
diff --git a/test/duobinary_minimal_example.m b/test/duobinary_minimal_example.m
index d61c044..c39183e 100644
--- a/test/duobinary_minimal_example.m
+++ b/test/duobinary_minimal_example.m
@@ -29,9 +29,6 @@ para.skip =0;
para.bruijn = 0;
para.reset_prms = 0;
para.method = 1;
-para.pcs = 0;
-para.shape_para = 0;
-para.rng_num = 0;
data_in = [];
global loop;
diff --git a/test/gpu_cpu_comparison.m b/test/gpu_cpu_comparison.m
new file mode 100644
index 0000000..02d40ba
--- /dev/null
+++ b/test/gpu_cpu_comparison.m
@@ -0,0 +1,349 @@
+%% GPU vs CPU Comparison Test for DP_Fiber
+% This script runs the fiber simulation with and without GPU acceleration
+% and compares the numerical results.
+
+% clear; clc;
+
+%% Setup (same as gpu_processing_dpfiber.m but simplified)
+s.wavelengthplan = calcWavelengthPlan(4, 400e9, 1310);
+link_length = 10;
+s.pmd = 0.1;
+s.gamma = 0.0023;
+
+s.M = 4;
+fsym = 112e9;
+fdac = 2*fsym;
+fadc = 120000000000;
+s.random_key = 1;
+
+% Laser / Modulator
+vbias_rel = 0.5;
+u_pi = 4.6;
+vbias = -vbias_rel*u_pi;
+laser_linewidth = 0e6;
+
+% DB Stuff
+duob_mode = db_mode.no_db;
+
+rcalpha = 0.05;
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
+
+s.chirpalpha = 0;
+s.p_launch = 3;
+s.p = "co";
+
+N = numel(s.wavelengthplan);
+
+switch s.p
+ case "co"
+ pol_rot = 100.*ones(1,N);
+ d_local = 0;
+ case "pair"
+ pol_rot = repmat([100,100,0,0],1,N/4);
+ d_local = 0;
+ case "alt"
+ pol_rot = repmat([100,0,100,0],1,N/4);
+ d_local = 0;
+ case "seg"
+ pol_rot = 100.*ones(1,N);
+ d_local = 3;
+ otherwise
+ error('Unknown fwm_mitigation_technique: %s', string(s.p));
+end
+
+f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
+margin = 5e12;
+f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
+f_nyq = f_span/2;
+
+kover = 4;
+upsample_required = f_nyq./(fdac*kover/2);
+upsample_pow = 2^nextpow2(upsample_required);
+
+s.f_opt = fdac*kover*upsample_pow;
+s.f_opt_nyq = s.f_opt/2;
+
+%% TX per channel
+for l = 1:N
+ [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
+ "fsym",fsym,"M",s.M,"order",15,"useprbs",0, ...
+ "fs_out",fdac, ...
+ "applyclipping",0,"clipfactor",1.5, ...
+ "applypulseform",1,"pulseformer",Pform, ...
+ "randkey",s.random_key+l, ...
+ "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
+ ).process();
+
+ Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
+ "filterType",filtertypes.gaussian,"active",true);
+
+ El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
+ "bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
+ "H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
+
+ clear Digi_sig
+
+ El_sig = El_sig.normalize("mode","oneone");
+ scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
+ El_sig = El_sig .* scaling;
+
+ Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
+ "lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
+ "linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
+
+ clear El_sig
+
+ signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
+
+ clear Eml_out Lp_awg
+end
+
+disp('Signal generated for all channels.');
+
+%% WDM mux + launch
+Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
+ "lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
+
+Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
+
+% Save input for both runs
+Opt_sig_input = Opt_sig_wdm;
+
+segment_length = 1;
+nSegments = link_length/segment_length;
+if abs(nSegments - round(nSegments)) > 1e-12
+ error('fiber_length_km=%g must be an integer multiple of segment_length=%g km.', link_length, segment_length);
+end
+nSegments = round(nSegments);
+
+zdw = 1310;
+randomize_D = true;
+
+if nSegments > 0
+ Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
+else
+ Dvec = [];
+end
+
+%% Run WITHOUT GPU
+fprintf('\n========== Running WITHOUT GPU (CPU) ==========\n');
+Opt_sig_cpu = Opt_sig_input;
+
+tic;
+for seg = 1:nSegments
+ fprintf('CPU Segment %d/%d \n',seg, nSegments);
+
+ Opt_sig_cpu = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",false).process(Opt_sig_cpu);
+end
+time_cpu = toc;
+fprintf('CPU Time: %.3f seconds\n', time_cpu);
+
+%% Run WITH GPU (Double Precision)
+fprintf('\n========== Running WITH GPU (Double Precision) ==========\n');
+Opt_sig_gpu_double = Opt_sig_input;
+
+tic;
+for seg = 1:nSegments
+ fprintf('GPU-Double Segment %d/%d \n',seg, nSegments);
+
+ Opt_sig_gpu_double = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",false).process(Opt_sig_gpu_double);
+end
+time_gpu_double = toc;
+fprintf('GPU Double Time: %.3f seconds\n', time_gpu_double);
+
+%% Run WITH GPU (Single Precision)
+fprintf('\n========== Running WITH GPU (Single Precision) ==========\n');
+Opt_sig_gpu_single = Opt_sig_input;
+
+tic;
+for seg = 1:nSegments
+ fprintf('GPU-Single Segment %d/%d \n',seg, nSegments);
+
+ Opt_sig_gpu_single = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",true).process(Opt_sig_gpu_single);
+end
+time_gpu_single = toc;
+fprintf('GPU Single Time: %.3f seconds\n', time_gpu_single);
+
+%% Compare Results
+fprintf('\n========== Numerical Comparison ==========\n');
+
+sig_cpu = Opt_sig_cpu.signal;
+sig_gpu_double = Opt_sig_gpu_double.signal;
+sig_gpu_single = Opt_sig_gpu_single.signal;
+
+% Check dimensions
+fprintf('CPU signal size: [%d x %d]\n', size(sig_cpu,1), size(sig_cpu,2));
+fprintf('GPU Double signal size: [%d x %d]\n', size(sig_gpu_double,1), size(sig_gpu_double,2));
+fprintf('GPU Single signal size: [%d x %d]\n', size(sig_gpu_single,1), size(sig_gpu_single,2));
+
+% CPU vs GPU Double
+fprintf('\n--- CPU vs GPU Double ---\n');
+diff_cpu_double = abs(sig_cpu - sig_gpu_double);
+max_diff_cpu_double = max(diff_cpu_double(:));
+mean_diff_cpu_double = mean(diff_cpu_double(:));
+rel_diff_cpu_double = max_diff_cpu_double / max(abs(sig_cpu(:)));
+fprintf('Max absolute difference: %.6e\n', max_diff_cpu_double);
+fprintf('Mean absolute difference: %.6e\n', mean_diff_cpu_double);
+fprintf('Max relative difference: %.6e\n', rel_diff_cpu_double);
+
+% CPU vs GPU Single
+fprintf('\n--- CPU vs GPU Single ---\n');
+diff_cpu_single = abs(sig_cpu - sig_gpu_single);
+max_diff_cpu_single = max(diff_cpu_single(:));
+mean_diff_cpu_single = mean(diff_cpu_single(:));
+rel_diff_cpu_single = max_diff_cpu_single / max(abs(sig_cpu(:)));
+fprintf('Max absolute difference: %.6e\n', max_diff_cpu_single);
+fprintf('Mean absolute difference: %.6e\n', mean_diff_cpu_single);
+fprintf('Max relative difference: %.6e\n', rel_diff_cpu_single);
+
+% GPU Double vs GPU Single
+fprintf('\n--- GPU Double vs GPU Single ---\n');
+diff_double_single = abs(sig_gpu_double - sig_gpu_single);
+max_diff_double_single = max(diff_double_single(:));
+mean_diff_double_single = mean(diff_double_single(:));
+rel_diff_double_single = max_diff_double_single / max(abs(sig_gpu_double(:)));
+fprintf('Max absolute difference: %.6e\n', max_diff_double_single);
+fprintf('Mean absolute difference: %.6e\n', mean_diff_double_single);
+fprintf('Max relative difference: %.6e\n', rel_diff_double_single);
+
+% Check tolerances
+fprintf('\n--- Tolerance Check ---\n');
+tol_double = 1e-10;
+tol_single = 1e-5; % Single precision has ~7 significant digits
+
+if max_diff_cpu_double < tol_double
+ fprintf('✓ CPU vs GPU Double: EQUIVALENT (diff < %.0e)\n', tol_double);
+else
+ fprintf('✗ CPU vs GPU Double: DIFFER beyond tolerance (%.0e)\n', tol_double);
+end
+
+if max_diff_cpu_single < tol_single
+ fprintf('✓ CPU vs GPU Single: ACCEPTABLE (diff < %.0e)\n', tol_single);
+else
+ fprintf('⚠ CPU vs GPU Single: Precision loss detected (diff = %.2e, tol = %.0e)\n', max_diff_cpu_single, tol_single);
+end
+
+% Performance comparison
+fprintf('\n========== Performance Summary ==========\n');
+fprintf('CPU Time: %.3f s\n', time_cpu);
+fprintf('GPU Double Time: %.3f s\n', time_gpu_double);
+fprintf('GPU Single Time: %.3f s\n', time_gpu_single);
+fprintf('\n');
+fprintf('Speedup (GPU Double vs CPU): %.2fx\n', time_cpu/time_gpu_double);
+fprintf('Speedup (GPU Single vs CPU): %.2fx\n', time_cpu/time_gpu_single);
+fprintf('Speedup (GPU Single vs GPU Double): %.2fx\n', time_gpu_double/time_gpu_single);
+
+%% ========== BER Comparison ==========
+% Process each fiber output through simplified receiver to check if
+% single-precision affects actual BER performance
+
+fprintf('\n========== BER Comparison ==========\n');
+fprintf('Processing signals through receiver chain...\n');
+
+% Receiver parameters
+rop = -7; % Received optical power [dBm]
+len_tr = 4096; % Training length
+mu_dc = 0.005;
+mu_ffe = [0.0001 0.0008 0.001];
+mu_dfe = 0.0004;
+
+% Helper function to process through receiver and get BER
+function ber = process_receiver(Opt_sig_fib, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode)
+
+% Demux single channel
+Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
+ "fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_fib);
+
+% ROP amplifier
+Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",rop).process(Opt_sig_demux{l});
+
+% Photodiode
+PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
+ "nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
+
+% Low-pass filter
+rx_bwl = 100e9;
+PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
+ "filterType",filtertypes.butterworth,"active",true).process(PD_sig);
+
+% Scope
+Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
+Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
+ "delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
+ "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
+ "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
+
+% Resample to 2 sps
+Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
+
+% Time sync
+[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
+Rx_sig = Scpe_cell{1};
+Rx_sig = Rx_sig.normalize("mode","rms");
+
+% FFE Equalizer
+ffe_order = [50, 0, 0];
+eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
+
+ffe_results = ffe(eq_ffe,s.M,Rx_sig,Symbols{l},Tx_bits{l}, ...
+ "precode_mode",duob_mode, ...
+ 'showAnalysis',0, ...
+ "postFFE",[], ...
+ "eth_style_symbol_mapping",0);
+
+ber = ffe_results.metrics.BER;
+end
+
+% Process each mode for channel 1
+l = 4; % Use first channel for comparison
+
+fprintf('Processing CPU result...\n');
+ber_cpu = process_receiver(Opt_sig_cpu, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
+
+fprintf('Processing GPU Double result...\n');
+ber_gpu_double = process_receiver(Opt_sig_gpu_double, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
+
+fprintf('Processing GPU Single result...\n');
+ber_gpu_single = process_receiver(Opt_sig_gpu_single, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
+
+% Display BER results
+fprintf('\n========== BER Results (Channel %d, ROP = %d dBm) ==========\n', l, rop);
+fprintf('CPU: BER = %.4e\n', ber_cpu);
+fprintf('GPU Double: BER = %.4e\n', ber_gpu_double);
+fprintf('GPU Single: BER = %.4e\n', ber_gpu_single);
+
+fprintf('\n--- BER Comparison ---\n');
+if ber_cpu == 0 && ber_gpu_double == 0 && ber_gpu_single == 0
+ fprintf('✓ All BERs are zero (no errors detected)\n');
+else
+ ber_diff_double = abs(ber_cpu - ber_gpu_double);
+ ber_diff_single = abs(ber_cpu - ber_gpu_single);
+ fprintf('|BER_cpu - BER_gpu_double| = %.4e\n', ber_diff_double);
+ fprintf('|BER_cpu - BER_gpu_single| = %.4e\n', ber_diff_single);
+
+ if ber_diff_double < 1e-6 && ber_diff_single < 1e-6
+ fprintf('✓ BER differences are negligible\n');
+ elseif ber_diff_single > ber_diff_double * 10
+ fprintf('⚠ Single precision shows measurable BER impact\n');
+ else
+ fprintf('✓ BER differences within acceptable range\n');
+ end
+end
+
+fprintf('\n========== Test Complete ==========\n');
diff --git a/test/gpu_processing_dpfiber.m b/test/gpu_processing_dpfiber.m
new file mode 100644
index 0000000..317833e
--- /dev/null
+++ b/test/gpu_processing_dpfiber.m
@@ -0,0 +1,265 @@
+
+
+s.wavelengthplan = calcWavelengthPlan(16, 400e9, 1310);
+N = numel(s.wavelengthplan);
+link_length = 10;
+s.pmd = 0.1;%0.1;
+s.gamma = 0.0023;
+
+s.M = 4;
+fsym = 112e9;
+fdac = 2*fsym;
+fadc = 120000000000;
+s.random_key = 1;
+
+% Laser / s.Modulator
+vbias_rel = 0.5;
+u_pi = 4.6;
+vbias = -vbias_rel*u_pi;
+laser_linewidth = 0e6;
+
+% DB Stuff
+duob_mode = db_mode.no_db;
+
+rcalpha = 0.05;
+Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
+
+s.chirpalpha = 0;
+
+s.p_launch = 3;
+s.p = "co";
+
+switch s.p
+ case "co"
+ pol_rot = 100.*ones(1,N);
+ d_local = 0;
+ case "pair"
+ pol_rot = repmat([100,100,0,0],1,N/4);
+ d_local = 0;
+ case "alt"
+ pol_rot = repmat([100,0,100,0],1,N/4);
+ d_local = 0;
+ case "seg"
+ pol_rot = 100.*ones(1,N);
+ d_local = 3;
+ otherwise
+ error('Unknown fwm_mitigation_technique: %s', string(s.p));
+end
+
+f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
+margin = 25e12; % some THz left and right
+f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
+f_nyq = f_span/2;
+
+kover = 4;
+upsample_required = f_nyq./(fdac*kover/2);
+upsample_pow = 2^nextpow2(upsample_required);
+
+s.f_opt = fdac*kover*upsample_pow;
+s.f_opt_nyq = s.f_opt/2;
+
+s.rop = -10:1:0;
+
+profile on
+
+%% ---------- TX per channel ----------
+for l = 1:N
+
+ [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
+ "fsym",fsym,"M",s.M,"order",17,"useprbs",0, ...
+ "fs_out",fdac, ...
+ "applyclipping",0,"clipfactor",1.5, ...
+ "applypulseform",1,"pulseformer",Pform, ...
+ "randkey",s.random_key+l, ...
+ "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
+ ).process();
+
+ Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
+ "filterType",filtertypes.gaussian,"active",true);
+
+ El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
+ "bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
+ "H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
+
+ % Digi_sig not needed after AWG
+ clear Digi_sig
+
+ % Electrical Driver Amplifier
+ El_sig = El_sig.normalize("mode","oneone");
+ scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
+ El_sig = El_sig .* scaling;
+
+ % E/O Conversion
+ Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
+ "lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
+ "linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
+
+ % El_sig not needed after EML
+ clear El_sig
+
+ signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
+
+ % Eml_out not needed after pol controller
+ clear Eml_out Lp_awg
+end
+
+disp('Signal generated for all channels.');
+
+%% ---------- WDM mux + launch ----------
+Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
+ "lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
+
+Opt_sig_wdm.spectrum();
+
+Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
+
+Opt_sig_wdm_fib = Opt_sig_wdm;
+
+segment_length = 1;
+nSegments = link_length/segment_length;
+if abs(nSegments - round(nSegments)) > 1e-12
+ error('fiber_length_km=%g must be an integer multiple of segment_length=%g km.', link_length, segment_length);
+end
+nSegments = round(nSegments);
+
+zdw = 1310;
+randomize_D = true;
+
+% Guard for 0 km: avoid calling getDispersionVector(0,...) if it doesn't support it
+if nSegments > 0
+ Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
+else
+ Dvec = [];
+end
+
+for seg = 1:nSegments
+
+ fprintf('Segment %d/%d \n',seg, nSegments);
+
+ Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
+ "beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
+ "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
+ "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",1).process(Opt_sig_wdm_fib);
+
+end
+
+profile off
+profile viewer
+
+Opt_sig_wdm_fib.spectrum();
+
+%% ========== BER Evaluation ==========
+fprintf('\n========== BER Evaluation ==========\n');
+fprintf('Processing signals through receiver chain...\n');
+
+% Receiver parameters
+len_tr = 4096; % Training length
+mu_dc = 0.005;
+mu_ffe = [0.0001 0.0008 0.001];
+mu_dfe = 0.0004;
+
+% Helper function to process through receiver and get BER
+function ber = process_receiver(Opt_sig_fib, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode)
+
+% Demux single channel
+Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
+ "fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_fib);
+
+% ROP amplifier
+Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
+ "amplification_db",rop).process(Opt_sig_demux{l});
+
+% Photodiode
+PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
+ "nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
+
+% Low-pass filter
+rx_bwl = 100e9;
+PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
+ "filterType",filtertypes.butterworth,"active",true).process(PD_sig);
+
+% Scope
+Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
+Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
+ "delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
+ "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
+ "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
+
+% Resample to 2 sps
+Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
+
+% Time sync
+[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
+Rx_sig = Scpe_cell{1};
+Rx_sig = Rx_sig.normalize("mode","rms");
+
+% FFE Equalizer
+ffe_order = [50, 0, 0];
+eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
+ "K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
+eq_ffe = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",6.624e-05,"mu_tr",0.058136,"order",50,"sps",2,"decide",0, "adaption",adaption_method.nlms,"dd_mode",1);
+
+ffe_results = ffe(eq_ffe,s.M,Rx_sig,Symbols{l},Tx_bits{l}, ...
+ "precode_mode",duob_mode, ...
+ 'showAnalysis',0, ...
+ "postFFE",[], ...
+ "eth_style_symbol_mapping",0);
+
+ber = ffe_results.metrics.BER;
+end
+
+% Process each ROP value for selected channels using parfor
+ber_results = zeros(length(s.rop), N);
+
+% Flatten loop for parfor: iterate over all (ROP, channel) combinations
+num_rop = length(s.rop);
+rop_vals = s.rop;
+ber_flat = zeros(num_rop * N, 1);
+
+parfor idx = 1:(num_rop * N)
+ % Convert linear index to (ri, l) subscripts
+ ri = ceil(idx / N);
+ l = mod(idx - 1, N) + 1;
+
+ fprintf('ROP %d dBm, Channel %d/%d\n', rop_vals(ri), l, N);
+ ber_flat(idx) = process_receiver(Opt_sig_wdm_fib, l, Symbols, Tx_bits, ...
+ fdac, kover, upsample_pow, fsym, fadc, rop_vals(ri), s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
+end
+
+% Reshape back to [num_rop × N] matrix
+ber_results = reshape(ber_flat, [N, num_rop]).';
+
+
+%% Display BER Results
+fprintf('\n========== BER Results ==========\n');
+fprintf('ROP [dBm] | ');
+for l = 1:N
+ fprintf('Ch%d | ', l);
+end
+fprintf('\n');
+
+for ri = 1:length(s.rop)
+ fprintf('%8d | ', s.rop(ri));
+ for l = 1:N
+ fprintf('%.2e | ', ber_results(ri, l));
+ end
+ fprintf('\n');
+end
+
+% Plot BER vs ROP
+figure;
+semilogy(s.rop, mean(ber_results, 2), '-o', 'LineWidth', 2);
+hold on;
+for l = 1:N
+ semilogy(s.rop, ber_results(:, l), '--', 'LineWidth', 1);
+end
+hold off;
+xlabel('ROP [dBm]');
+ylabel('BER');
+title('BER vs Received Optical Power (GPU Single Precision)');
+legend(['Mean', arrayfun(@(x) sprintf('Ch%d', x), 1:N, 'UniformOutput', false)]);
+grid on;
+
+fprintf('\n========== Test Complete ==========\n');
diff --git a/test/gpu_processing_test.m b/test/gpu_processing_test.m
new file mode 100644
index 0000000..b08c03f
--- /dev/null
+++ b/test/gpu_processing_test.m
@@ -0,0 +1,70 @@
+% 1. Setup Data OUTSIDE the timer
+d = gpuDevice;
+N = 10000;
+
+fprintf('Preparing data...\n');
+A_cpu_double = rand(N, N); % Create double on CPU
+A_cpu_single = single(A_cpu_double); % Create single on CPU
+
+% Warmup run (wakes up the GPU from idle state)
+A_warm = gpuArray.rand(1000, 1000, 'single');
+B_warm = A_warm * A_warm;
+wait(d);
+
+fprintf('------------------------------------------------\n');
+
+% TEST 1: Double Precision (The "Slow" way)
+% We move data to GPU first so we only measure calculation time
+A_gpu_double = gpuArray(A_cpu_double);
+wait(d); % Ensure transfer is done before starting timer
+
+fprintf('Running DOUBLE precision test... ');
+tic;
+B_gpu = A_gpu_double * A_gpu_double;
+wait(d); % FORCE MATLAB TO WAIT FOR GPU
+time_double = toc;
+fprintf('Done.\n');
+fprintf('Double Precision Time: %.4f seconds\n', time_double);
+
+% TEST 2: Single Precision (The "Fast" way)
+A_gpu_single = gpuArray(A_cpu_single);
+wait(d); % Ensure transfer is done
+
+fprintf('Running SINGLE precision test... ');
+tic;
+B_gpu = A_gpu_single * A_gpu_single;
+wait(d); % FORCE MATLAB TO WAIT FOR GPU
+time_single = toc;
+fprintf('Done.\n');
+fprintf('Single Precision Time: %.4f seconds\n', time_single);
+
+% Calculate Speedup
+fprintf('------------------------------------------------\n');
+fprintf('Speedup Factor using single precision: %.2fx\n', time_double / time_single);
+
+%
+% Create 10,000 small matrices (10x10) stacked in a 3D array
+A_stack = gpuArray.rand(10, 10, 10000, 'single');
+B_stack = gpuArray.rand(10, 10, 10000, 'single');
+
+% BAD: Looping (GPU overhead kills you)
+tic;
+for i=1:10000
+ C(:,:,i) = A_stack(:,:,i) * B_stack(:,:,i);
+end
+wait(d);
+loop_time = toc;
+
+% GOOD: Pagefun (Executes all 10,000 mults simultaneously)
+tic;
+C_stack = pagefun(@mtimes, A_stack, B_stack);
+wait(d);
+pagefun_time = toc;
+
+fprintf('------------------------------------------------\n');
+fprintf('Speedup Factor using pagefun: %.2fx\n', loop_time / pagefun_time);
+
+
+fprintf('Total VRAM: %.2f GB\n', d.TotalMemory / 1e9);
+fprintf('Available VRAM: %.2f GB\n', d.AvailableMemory / 1e9);
+fprintf('Usage: %.1f%%\n', 100 * (1 - d.AvailableMemory / d.TotalMemory));
\ No newline at end of file