diff --git a/Classes/04_DSP/Equalizer/EQ.m b/Classes/04_DSP/Equalizer/EQ.m index a03d66b..f9d58d2 100644 --- a/Classes/04_DSP/Equalizer/EQ.m +++ b/Classes/04_DSP/Equalizer/EQ.m @@ -10,6 +10,10 @@ 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 @@ -68,6 +72,10 @@ 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 @@ -438,6 +446,43 @@ 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); @@ -739,3 +784,6 @@ 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 168b837..9965525 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; %feasible state transitions + obj.nFeasible = obj.nStates * obj.S; % --- Trellis mapping obj.trellis_states = reshape(obj.constellation,1,[]); diff --git a/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m index fe8f371..25dd729 100644 --- a/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m +++ b/projects/FSO_transmission/Evaluation Scripts/first_analysis_tr_mf.m @@ -135,10 +135,10 @@ 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, 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); + 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 @@ -232,40 +232,40 @@ for our_signal = 1 % 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); + % 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); % - % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style); + % [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',ml_mlse_results.metrics.BER); + % fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER); % fprintf('Paper: %.1e \n \n',ber_in_paper); % else - % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER); + % 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",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); + 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/FSO_timing_recovery_minimal_example.m b/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m index 36884ff..031ec50 100644 --- a/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m +++ b/projects/FSO_transmission/FSO_timing_recovery_minimal_example.m @@ -212,40 +212,40 @@ for our_signal = 1 % 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); + % 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); % - % [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode,"eth_style_symbol_mapping",mapping_style); + % [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',ml_mlse_results.metrics.BER); + % fprintf('Our Signal: %.1e \n',mlse_results.metrics.BER); % fprintf('Paper: %.1e \n \n',ber_in_paper); % else - % fprintf('Their EQ: %.1e \n',ml_mlse_results.metrics.BER); + % 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",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); + 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/tore --source rescue-premerge -- Classes b/tore --source rescue-premerge -- Classes new file mode 100644 index 0000000..36a0be2 --- /dev/null +++ b/tore --source rescue-premerge -- Classes @@ -0,0 +1,6961 @@ +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 +  + % + %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 diff --git a/tore --source rescue-premerge -- CUsersmagfDesktopDesktopMATLAB-ZeugsSilas DSPimdd_simulationClasses b/tore --source rescue-premerge -- CUsersmagfDesktopDesktopMATLAB-ZeugsSilas DSPimdd_simulationClasses new file mode 100644 index 0000000..9cb587a --- /dev/null +++ b/tore --source rescue-premerge -- CUsersmagfDesktopDesktopMATLAB-ZeugsSilas DSPimdd_simulationClasses @@ -0,0 +1,150 @@ +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 + % Ta \ No newline at end of file