diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index c84ebf6..a21e71c 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -344,10 +344,13 @@ classdef Signal arguments obj - options.fignum + options.fignum = 2025 options.displayname = ""; options.color = []; + options.linestyle = '-'; options.normalizeToNyquist = 0; + options.addDCoffset = 0; + options.normalizeToDC = 0; options.normalizeTo0dB = 0; options.max_num_lines = []; % Leave empty or omit to disable line rotation options.fft_length = []; @@ -360,6 +363,8 @@ classdef Signal options.fft_length = 2^(nextpow2(length(obj.signal))-9); end + + if options.normalizeToNyquist == 0 [p_lin,f_Hz] = pwelch(obj.signal, hanning(options.fft_length), ... options.fft_length/2, options.fft_length, ... @@ -373,6 +378,8 @@ classdef Signal % We'll keep f_rad for the x-axis in that mode. end + % p_lin = movmean(p_lin,4); + if options.normalizeTo0dB p_lin = p_lin ./ max(p_lin); p_dbm = 10*log10(p_lin); % normalized to 0 dB @@ -415,11 +422,20 @@ classdef Signal ax = gca; hold on + p_dbm = p_dbm+options.addDCoffset; + + if options.normalizeToDC + [~,min_idx]=min(abs(f_GHz)); + pow_at_dc = p_dbm(min_idx); + p_dbm = p_dbm-pow_at_dc; + end + % p_dbm = movmean(p_dbm,10); + for s = 1:min(size(p_dbm)) if isempty(options.color) plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1); else - plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1, 'Color', options.color); + plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1, 'Color', options.color,'LineStyle',options.linestyle); end end @@ -449,21 +465,25 @@ classdef Signal ylabel(ylab); + % --- Y-Axis scaling (auto with margin) --- + y_min = min(p_dbm(:)); + y_max = max(p_dbm(:)); + + % Add 5% dynamic range margin on both sides + y_range = y_max - y_min; + if y_range == 0 + y_range = 10; % fallback if flat + end + + y_margin = 0.05 * y_range; + ylim([y_min - y_margin, y_max + y_margin]); + + % Set ticks automatically, avoid overpopulation try - ylim([max(min(floor(min(p_dbm))-3, ax.YLim(1)),-40), min(max(ceil(max(p_dbm))+3, ax.YLim(2)),10)]); - catch - ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]); + yticks(round(linspace(y_min, y_max, min(10, max(4, ceil(y_range/10)))))); end - - if options.normalizeTo0dB - ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]); - else - ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]); - end - - yticks(-200:10:10); grid on; - legend + end @@ -961,8 +981,8 @@ classdef Signal maxA = max(sig(100:end-100))*1.3; minA = min(sig(100:end-100))*1.3; - % maxA = 0.0015; - % minA = 0; + maxA = 0.12; + minA = -0.08; difference= maxA-minA; @@ -1012,7 +1032,7 @@ classdef Signal % add information - if 1 + if 0 pwr_dbm = round(obj.power,3); pwr_lin = obj.power("unit",power_notation.W); @@ -1121,20 +1141,20 @@ classdef Signal end - yticks(linspace(0,histpoints,6)); - y_tickstring = sprintfc('%.2f', y_tickstring); - yticklabels(y_tickstring); - - xticks(linspace(0,histpoints_horizontal,6)) - x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12); - xticklabels(x_tickstring); - grid off + end + yticks(linspace(0,histpoints,6)); + y_tickstring = sprintfc('%.2f', y_tickstring); + yticklabels(y_tickstring); + xticks(linspace(0,histpoints_horizontal,6)) + x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12); + xticklabels(x_tickstring); + % end % disp('h'); diff --git a/Classes/01_transmit/ChannelFreqResp.m b/Classes/01_transmit/ChannelFreqResp.m index 0c9fda8..e6c8a2a 100644 --- a/Classes/01_transmit/ChannelFreqResp.m +++ b/Classes/01_transmit/ChannelFreqResp.m @@ -139,6 +139,8 @@ classdef ChannelFreqResp < handle fnew = linspace(0,fstarget/2,length(Target)/2+1); fnew = fnew(2:end-1); + + % Old frequency axis (should be much coarser) idx_old = find((obj.faxis > 0) .* (obj.faxis < fstarget/2)); %positions of all Frequencies smaller than fs/2 int_fold = obj.faxis(idx_old); %old frequencies from 0 to fs/2 @@ -149,6 +151,10 @@ classdef ChannelFreqResp < handle % interpolate the frequency response that had a coarse frequency resolution (e.g. 256 bins) to the current frequency resolution (e.g. 21843 bins) iH = interp1(int_fold, real(H_inv(idx_old)) ,fnew, 'linear') + 1i*interp1(int_fold, imag(H_inv(idx_old)) ,fnew, 'linear'); + if 0 + figure(7);hold on;plot(fnew,20*log10(abs(iH))) + end + % set all NaN values to the fist/ last non-NaN value nH = find(~isnan(iH),1,'first'); iH(1:nH)=iH(nH); @@ -171,6 +177,8 @@ classdef ChannelFreqResp < handle % five frequencies -> should be the vaue at f=0=DC component? iH = iH./mean(abs(iH)); %why 1:5?? + dc = 20*log10(abs(mean(abs(iH(1:5))))); + % set maximum amplification % set als values higher than hmax to hmax and keep the % phase information by multiplication with respective @@ -206,7 +214,7 @@ classdef ChannelFreqResp < handle function plot(obj) - figure(55); + figure(); clf; Havg = obj.H; @@ -225,7 +233,7 @@ classdef ChannelFreqResp < handle xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on; - figure(56); + figure(); clf; @@ -246,7 +254,7 @@ classdef ChannelFreqResp < handle xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on; %%% plot for publication - figure(1234); + figure(101); hold all; box on; title('Magnitude Freq. Response'); @@ -269,6 +277,25 @@ classdef ChannelFreqResp < handle legend('Interpreter','latex') grid on; + % + figure(100); hold on; + Havg = obj.H; + Havg = Havg./max(Havg); + Hall = obj.H_all; + + for i = 1:size(Hall,1) + Hall(i,:) = Hall(i,:)./max(Hall(i,:)); + end + + %1) + col = cbrewer2('Paired',8); + hold all;box on;title('Magnitude Freq. Response'); + plot(obj.faxis/1e9, 20*log10(abs(Hall)),'linewidth',0.1,'LineStyle','-','Color',col(1,:),'HandleVisibility','off') ; + xlim([0.2 .5*max(obj.faxis)*1e-9]); + plot(obj.faxis/1e9, 20*log10(abs(Havg)),'LineWidth',2,'Color',col(2,:)); + grid on; + + end diff --git a/Classes/04_DSP/Equalizer/FFE.m b/Classes/04_DSP/Equalizer/FFE.m index 5920e89..18442d6 100644 --- a/Classes/04_DSP/Equalizer/FFE.m +++ b/Classes/04_DSP/Equalizer/FFE.m @@ -131,7 +131,7 @@ classdef FFE < handle mask = ones(obj.order,1); maincursor_pos=ceil(length(obj.e)/2); always_ideal_decision = 0; - save_debug = 1; + save_debug = 0; grad =0; weight = 0; update = 0; diff --git a/Classes/04_DSP/Equalizer/FFE_MLSE.m b/Classes/04_DSP/Equalizer/FFE_MLSE.m new file mode 100644 index 0000000..d94da34 --- /dev/null +++ b/Classes/04_DSP/Equalizer/FFE_MLSE.m @@ -0,0 +1,233 @@ +classdef FFE_MLSE < handle + % Implementation of plain and simple FFE. + % 1) Training mode (stable performance when you use NLMS) + % 2) Decision directed mode + + %LMS: mu in order of 0.0001 for acceptable convergence speed + %NLMS: mu in order of 0.01 for acceptable convergence speed + %RLS: mu is lambda -> 0.99 -> 1 (has a strong dependency on this! use a loop to find out best values) + + % FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode); + + properties + sps % usually 2 + order + e + e_tr + error + + len_tr + mu_tr + epochs_tr + + dd_mode % 1 or 0 to set DD-mode on or off + mu_dd %weight update in dd mode + epochs_dd + + constellation + + L %viterbi memory length + + alpha + DIR + DIR_flip + trellis_states + + traceback_depth + end + + methods + function obj = FFE_MLSE(options) + arguments(Input) + + options.sps = 2; + options.order = 15; + + options.len_tr = 4096; + options.mu_tr = 0; + options.epochs_tr = 5; + + options.dd_mode = 1; + options.mu_dd = 1e-5; + options.epochs_dd = 5; + + options.traceback_depth = 1024; + + options.L = 1 + + end + + fn = fieldnames(options); + for n = 1:numel(fn) + obj.(fn{n}) = options.(fn{n}); + end + + obj.e = zeros(obj.order,1); + obj.error = 0; + + end + + function [X,X_viterbi] = process(obj, X, D) + + % actual processing of the signal (steps 1. - 3.) + % 1 normalize RMS + X = X.normalize("mode","rms"); + + obj.constellation = unique(D.signal); + + if length(X)/length(D) ~= obj.sps + warning('Signal length does not fit to reference!'); + end + + % Training Mode + n = obj.len_tr; + training = 1; + obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_dd,n,training); + obj.e_tr = obj.e; + + % Decision Directed Mode + n = X.length; + training = 0; + [y,y_vit]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,n,training); + + X_viterbi = X; + + X.signal = y; + X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym + lbdesc = [num2str(obj.order),' tap FFE']; + X = X.logbookentry(lbdesc); % append to logbook + + X_viterbi.signal = y_vit; + X_viterbi.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym + lbdesc = [num2str(obj.order),'order FFE + PF + Viterbi']; + X_viterbi = X_viterbi.logbookentry(lbdesc); % append to logbook + + + end + + function [y,y_vit] = equalize(obj,x,d,mu,epochs,N,training) + % ============================================================== + % FFE + Whitening + Viterbi Equalizer (reference implementation) + % ============================================================== + + % --- Input padding and preallocation + x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)]; + N_ = N / obj.sps; + y = zeros(N_,1); + y_white = zeros(N_,1); + + for epoch = 1:epochs + + % ============================================================== + % INITIALIZATION (only before final epoch and detection mode) + % ============================================================== + if epoch == epochs && ~training + + % --- Parameters + S = numel(unique(d)); + L = obj.L; + nStates = S^L; + nFeasible = S^(L-1)*S; + + % --- Trellis setup + obj.DIR = arburg(y-d, L); + obj.DIR_flip = flip(obj.DIR); + obj.trellis_states = reshape(unique(d),1,[]); + + pre_comb_mat = repmat(obj.trellis_states, L, 1); + pre_comb_cell = mat2cell(pre_comb_mat, ones(1,L), size(pre_comb_mat,2)); + combs = fliplr(combvec(pre_comb_cell{:}).'); + first_sym = combs(:,1); + last_sym = combs(:,end); + nStates = size(combs,1); + + noise_free_received = inf(nStates,nStates); + valid = false(nStates); + + for from = 1:nStates + for to = 1:nStates + if all(combs(to,2:end) == combs(from,1:end-1)) + noise_free_received(to,from) = ... + dot(combs(to,:), obj.DIR_flip(end:-1:2)) + last_sym(from)*obj.DIR_flip(1); + valid(to,from) = true; + end + end + end + + nf_vec = noise_free_received(valid); + [valid_to, valid_from] = find(valid); + from_per_to = arrayfun(@(to)find(valid(to,:)), 1:nStates, 'UniformOutput', false); + + % --- Noise stats + y_ideal = conv(d(:), obj.DIR(:), "same"); + sigma2 = mean(abs(y - y_ideal).^2); + inv2s2 = 1/(2*sigma2); + + % --- Vector initialization + bm_vec = zeros(1,nFeasible); + pm = zeros(nStates,1); + pm_next = zeros(nStates,1); + bm_fw = zeros(nStates,nStates,length(y)); + zi = zeros(max(numel(obj.DIR)-1,0),1); + end + + % ============================================================== + % RUNTIME LOOP (FFE update + Viterbi detection in last epoch) + % ============================================================== + symbol = 0; + for sample = 1:obj.sps:N + symbol = symbol + 1; + + % --- FFE output + U = x(obj.order+sample-1:-1:sample); + y(symbol,1) = obj.e.' * U; + + % --- Decision + if training + d_hat(symbol,1) = d(symbol); + else + [~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); + d_hat(symbol,1) = obj.constellation(symbol_idx); + end + + % --- LMS weight update + err(symbol) = d_hat(symbol) - y(symbol); + obj.e = obj.e + mu * (err(symbol) * U); + + % --- Whitening + Viterbi (final epoch only) + if epoch == epochs && ~training + + [y_white(symbol), zi] = filter(obj.DIR,1,y(symbol), zi); + + if symbol == 1 + pm = -inf(nStates,nStates); + pm(:,1:nStates) = 0; + else + bm_vec = -(y_white(symbol) - nf_vec).^2 * inv2s2; + bm_mat = -inf(nStates,nStates); + bm_mat(valid) = bm_vec; + + pm_new = pm + bm_mat; + [pm_survive(:,symbol), pm_survivor_fw_idx(:,symbol)] = max(pm_new,[],2); + pm = repmat(pm_survive(:,symbol).', nStates,1); + end + + % --- Traceback + if mod(symbol,obj.traceback_depth) == 0 + [~,viterbi_path(symbol)] = max(pm_survive(:,symbol)); + for n = symbol:-1:symbol-obj.traceback_depth+2 + viterbi_path(n-1) = pm_survivor_fw_idx(viterbi_path(n),n); + end + end + end + end + + % --- Final reconstruction + if epoch == epochs && ~training + y_vit = first_sym(viterbi_path); + end + end + end + + end +end \ No newline at end of file diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m new file mode 100644 index 0000000..3f584c3 --- /dev/null +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -0,0 +1,355 @@ +classdef ML_MLSE < handle + % --------------------------------------------------------------------- + % W. Lanneer and Y. Lefevre, + % “Machine Learning-Based Pre-Equalizers for Maximum Likelihood + % Sequence Estimation in High-Speed PONs,” EUSIPCO 2023 + % --------------------------------------------------------------------- + % This implementation reproduces the closed-loop ML-based + % pre-equalizer training for MLSE, supporting both training and + % detection (decision-directed) modes. + % --------------------------------------------------------------------- + + properties + sps + order + e + e_tr + error + + len_tr + mu_tr + epochs_tr + + dd_mode + mu_dd + epochs_dd + adaptive_mu + + constellation + L + alpha + DIR + DIR_flip + trellis_states + traceback_depth + delta + + % Internal variables + S + Nf + nStates + nFeasible + combs + first_sym + last_sym + valid + valid_to_idx + valid_from_idx + w + + % Fast lookup + nSym + key_table + trans_index + true_to_state_idx + + % Debug metrics + ber = [] + ce = ones(1,1) + end + + methods + function obj = ML_MLSE(options) + arguments(Input) + options.sps = 2; + options.order = 15; + options.len_tr = 4096; + options.mu_tr = 0.001; + options.epochs_tr = 5; + options.dd_mode = 1; + options.mu_dd = 1e-5; + options.epochs_dd = 5; + options.adaptive_mu = 1; + options.delta = 0; + options.traceback_depth = 1024; + options.L = 1; + end + + fn = fieldnames(options); + for n = 1:numel(fn) + obj.(fn{n}) = options.(fn{n}); + end + + obj.e = zeros(obj.order,1); + obj.error = 0; + end + + % ============================================================== + % PROCESS + % ============================================================== + function [X,X_viterbi] = process(obj, X, D) + % Normalize input RMS + X = X.normalize("mode","rms"); + obj.constellation = sort(unique(D.signal),'ascend'); + obj.nSym = numel(obj.constellation); + + if length(X)/length(D) ~= obj.sps + warning('Signal length does not fit to reference!'); + end + + % --- Parameters + obj.S = obj.nSym; + obj.Nf = obj.order * obj.sps; + obj.nStates = obj.S^obj.L; + obj.nFeasible = obj.nStates * obj.S; + + % --- Trellis mapping + obj.trellis_states = reshape(obj.constellation,1,[]); + pre_comb_mat = repmat(obj.trellis_states, obj.L, 1); + pre_comb_cell = mat2cell(pre_comb_mat, ones(1,obj.L), size(pre_comb_mat,2)); + obj.combs = fliplr(combvec(pre_comb_cell{:}).'); + obj.first_sym = obj.combs(:,1); + obj.last_sym = obj.combs(:,end); + obj.nStates = size(obj.combs,1); + + % --- Valid transitions + obj.valid = false(obj.nStates); + for from = 1:obj.nStates + for to = 1:obj.nStates + if all(obj.combs(to,2:end) == obj.combs(from,1:end-1)) + obj.valid(to,from) = true; + end + end + end + [obj.valid_to_idx,obj.valid_from_idx] = find(obj.valid); + + % --- Initialize weights + if isempty(obj.w) || any(size(obj.w) ~= [obj.Nf+1,obj.nFeasible]) + obj.w = randn(obj.Nf+1,obj.nFeasible); + end + + % --- Fast lookup tables + [~, sym_idx_mat] = ismember(obj.combs, obj.constellation); + key_vals = 1 + sum((sym_idx_mat - 1) .* (obj.nSym .^ (0:obj.L-1)), 2); + max_key = obj.nSym^obj.L; + obj.key_table = zeros(max_key,1,'uint32'); + obj.key_table(key_vals) = 1:obj.nStates; + + obj.trans_index = sparse(obj.nStates,obj.nStates); + for i = 1:length(obj.valid_from_idx) + f = obj.valid_from_idx(i); + t = obj.valid_to_idx(i); + obj.trans_index(t,f) = i; + end + + % ============================================================== + % TRAINING + % ============================================================== + fprintf('\n--- Training mode ---\n'); + obj.equalize(X.signal, D.signal, obj.mu_tr, obj.epochs_tr, obj.len_tr, true); + obj.e_tr = obj.e; + + % ============================================================== + % DECISION-DIRECTED / TESTING + % ============================================================== + fprintf('--- Decision-directed / detection mode ---\n'); + [y, y_vit] = obj.equalize(X.signal, D.signal, obj.mu_dd, obj.epochs_dd, X.length, false); + + X_viterbi = X; + X.signal = y; + X_viterbi.signal = y_vit; + end + + % ============================================================== + % EQUALIZE + % ============================================================== + function [y,y_ref] = equalize(obj,x,d,mu,epochs,N,training) + debug = 0; + showPlots = 0; + y = zeros(N,1); + nSymbols = ceil(N/obj.sps); + + for epoch = 1:epochs + pm = zeros(obj.nStates,1); + pred = zeros(nSymbols,obj.nStates,'uint32'); + pm_sto = nan(obj.nStates,nSymbols,'like',pm); + CE_accum = 0; + + start_sample = 1; + end_sample = N; + start_symbol = 1 + floor((start_sample - 1)/obj.sps); + + % --- initialize true state + if numel(d) >= obj.L && start_symbol >= obj.L + init_seq = d(start_symbol-obj.L+1:start_symbol); + key_init = obj.seq2key(init_seq); + true_to_state_idx = obj.key_table(key_init); + if true_to_state_idx==0, true_to_state_idx=1; end + else + true_to_state_idx = uint32(1); + end + + for sample = start_sample:obj.sps:end_sample + symbol = (sample - start_sample)/obj.sps + 1; + sym_idx = start_symbol + (symbol - 1); + + % --- Observation window (with delta) + i1 = sample - obj.Nf + 1 + obj.delta; + i2 = sample + obj.delta; + buf = x(max(1,i1):min(length(x),i2)); + padL = max(0,1 - i1); + padR = max(0,i2 - length(x)); + yk = [zeros(padL,1); buf(:); zeros(padR,1)]; + yk = [yk;1]; + + % --- Branch metrics + c_hat = (yk.' * obj.w).'; + pm = pm - min(pm); + v_tilde = pm(obj.valid_from_idx) + c_hat; + + % --- allocate once + if epoch==1 && symbol==1 + obj.true_to_state_idx = ones(ceil(N/obj.sps),1,'uint32'); + end + + % --- previous "to" becomes "from" + if symbol>1 + true_from_state_idx = obj.true_to_state_idx(symbol-1); + else + true_from_state_idx = 1; + end + + % --- compute or reuse "to" state + if epoch==1 + if sym_idx>=obj.L + key_to = obj.seq2key(d(sym_idx-obj.L+1:sym_idx)); + state_idx = obj.key_table(key_to); + if state_idx==0 + state_idx = true_from_state_idx; + end + obj.true_to_state_idx(symbol) = state_idx; + else + obj.true_to_state_idx(symbol) = true_from_state_idx; + end + end + true_to_state_idx = obj.true_to_state_idx(symbol); + + % --- fast Dirac creation + dirac = zeros(obj.nFeasible,1); + trans_idx = obj.trans_index(true_to_state_idx,true_from_state_idx); + if trans_idx~=0 + dirac(trans_idx)=1; + end + + % =================================================================== + % TRAINING MODE (weight update) + % =================================================================== + if training + % --- Softmax and CE + v_shift = -(v_tilde - min(v_tilde)); + v_shift = min(v_shift,100); + expv = exp(v_shift); + p = expv./(sum(expv)+eps); + CE_symbol(symbol) = -log(p(dirac==1)+eps); + + % --- CE smoothing and adaptive μ + if sym_idx>obj.L + CE_smooth(symbol)=0.01*CE_symbol(symbol)+0.99*CE_symbol(symbol-1); + else + CE_smooth(symbol)=CE_symbol(symbol); + end + CE_accum=CE_accum+CE_symbol(symbol); + + % --- Gradient update + dmp=(dirac-p)'; + dL_Dw=(yk).*dmp; + if sym_idx>=obj.L + if obj.adaptive_mu + mu_eff=CE_smooth(symbol); + mu_eff=max(min(mu_eff,0.2),1e-4); + else + mu_eff=mu; + end + obj.w=obj.w - mu_eff.*dL_Dw; + end + end + + % =================================================================== + % DECODING MODE (Viterbi only) + % =================================================================== + % Compare-Select (always executed) + vmat=inf(obj.nStates,obj.nStates); + vmat(obj.valid)=v_tilde; + [pm_next,pred(symbol,:)]=min(vmat,[],2); + pm_next=pm_next-min(pm_next); + pm=pm_next; + pm_sto(:,symbol)=pm; + end + + % --- Traceback + [~,s_end]=min(pm); + vpath=zeros(symbol,1,'uint32'); + vpath(symbol)=s_end; + for n=symbol:-1:2 + vpath(n-1)=pred(n,vpath(n)); + end + + y_ref=d(start_symbol:end); + y=obj.first_sym(vpath); + + % --- BER/CE reporting and plots + if training + err=sum(y~=y_ref(1:length(y))); + ser=err/length(y); + try + ref_bits=PAMmapper(obj.S,0).demap(y_ref(1:length(y))); + eq_bits=PAMmapper(obj.S,0).demap(y); + [~,~,ber,~]=calc_ber(ref_bits,eq_bits,"skip_front",10,"skip_end",10,"returnErrorLocation",1); + fprintf('Epoch %d - BER: %.2e\n',epoch,ber); + obj.ber(epoch)=ber; + catch + fprintf('Epoch %d - SER: %.2e\n',epoch,ser); + obj.ber(epoch)=ser; + end + obj.ce(epoch)=CE_accum/symbol; + + if debug && mod(epoch,10)==1 && showPlots + figure(10);clf + subplot(3,2,1:2); + imagesc(obj.w);axis xy;colorbar;title('Filter W'); + subplot(3,2,3); + vtilde_mat=NaN(obj.nStates,obj.nStates); + vtilde_mat(obj.valid)=v_tilde; + imagesc(vtilde_mat);axis xy;colorbar;title('Path Metrics (v\_tilde)'); + subplot(3,2,4); + plot(1:symbol,pm_sto);title('Path Metric Evolution'); + subplot(3,2,5);hold on; + scatter(1:symbol,CE_symbol,1,'.'); + scatter(1:symbol,CE_smooth,1,'.'); + title('Cross Entropy'); + subplot(3,2,6);hold on; + yyaxis left + scatter(1:length(obj.ce),obj.ce,10,'s','filled'); + ylabel('Cross Entropy'); + yyaxis right + scatter(1:length(obj.ber),obj.ber,10,'d','filled'); + set(gca,'YScale','log'); + ylabel('BER (log)'); + xlabel('Epoch');grid on; + title('Convergence'); + drawnow; + end + end + end + end + + % ============================================================== + % Helper: Sequence → key (always scalar) + % ============================================================== + function key = seq2key(obj, seq) + [~, idx] = ismember(flip(seq), obj.constellation); + pow = (obj.nSym .^ (0:obj.L-1)).'; + key = 1 + sum((idx(:) - 1) .* pow); + end + end +end diff --git a/Classes/04_DSP/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index e51d61b..59ef211 100644 --- a/Classes/04_DSP/Sequence Detection/MLSE.m +++ b/Classes/04_DSP/Sequence Detection/MLSE.m @@ -6,6 +6,10 @@ classdef MLSE < handle DIR trellis_states duobinary_output + trellis_state_mode + trellis_exclusion + debug + scale_mode end methods (Access=public) @@ -19,7 +23,10 @@ classdef MLSE < handle options.DIR double = [1]; options.trellis_states double = [-3 -1 1 3]; options.duobinary_output logical = false; - + options.trellis_state_mode = 2; + options.trellis_exclusion = 0; + options.scale_mode = 2; + options.debug = 0; end % @@ -33,6 +40,12 @@ classdef MLSE < handle function [signalclass_hd,LLR,GMI] = process(obj,signalclass,ref_symbolclass) + arguments + obj + signalclass + ref_symbolclass + end + data_in = signalclass.signal; shape_in = size(data_in); data_ref = ref_symbolclass.signal; @@ -54,18 +67,25 @@ classdef MLSE < handle function [VITERBI_ESTIMATION_SYMBOLS,LLR_maxlogmap,GMI] = process_(obj,data_in,data_ref) - debug = 0; + + arguments + obj + data_in + data_ref + end - trellis_state_mode = 2; % General: States should match the target states of the prev. EQ (EQ's job was to reduce the error between signal and the target) + debug = obj.debug; + + trellis_state_mode = obj.trellis_state_mode; % General: States should match the target states of the prev. EQ (EQ's job was to reduce the error between signal and the target) % 0 = use provided states (MUST provide the correct states); % 1 = normalize to = 1 rms; % 2 = use target symbols; % 3 = use statistical levels % 3 analyzes avg of rx signal levels - can help with nonlinear impairments - trellis_exclusion = 0; % PAM-6 only (only if data is NOT precoded!) + trellis_exclusion = obj.trellis_exclusion; % PAM-6 only (only if data is NOT precoded!) - scale_mode = 2; % scale_mode: + scale_mode = obj.scale_mode; % scale_mode: % 0 = no scaling, % 1 = RMS→scale MODEL, % 2 = MMSE/time-corr→scale MODEL, @@ -98,7 +118,7 @@ classdef MLSE < handle elseif trellis_state_mode == 2 %simply use the states from the ref signal (should be a robust option) - obj.trellis_states = reshape(unique(data_ref),size(obj.trellis_states)); + obj.trellis_states = reshape(unique(data_ref),1,length(unique(data_ref))); elseif trellis_state_mode == 3 %use_statistical_levels @@ -257,14 +277,107 @@ classdef MLSE < handle if debug - alpha_ = alpha - min(alpha) + eps; - figure();hold on; - n = 10; - scatter(1:n,obj.trellis_states(repmat([1:numel(obj.trellis_states)]',1,n)),abs(alpha_(:,end-n+1:end)),'Marker','o','LineWidth',1); - scatter(1:n,obj.trellis_states(viterbi_path(end-n+1:end)),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','green'); - % scatter(1:n,data_ref(end-n+1:end),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','red'); - yticks(obj.trellis_states); - ylim([min(obj.trellis_states)-1 max(obj.trellis_states)+1]); + % alpha_ = alpha - min(alpha) + eps; + % figure();hold on; + % n = 10; + % scatter(1:n,obj.trellis_states(repmat([1:numel(obj.trellis_states)]',1,n)),abs(alpha_(:,end-n+1:end)),'Marker','o','LineWidth',1); + % scatter(1:n,obj.trellis_states(viterbi_path(end-n+1:end)),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','green'); + % % scatter(1:n,data_ref(end-n+1:end),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','red'); + % yticks(obj.trellis_states); + % ylim([min(obj.trellis_states)-1 max(obj.trellis_states)+1]); + + %% ----- Build true path from known sequence (data_ref) ----- + % Memory length used by VA (L = length(obj.DIR)-1 symbols stored in state) + L = size(combs,2); % each row of combs is the L-tap state vector + N = length(data_in); + + % Map each trellis level to an index (1..M) + [~, level_to_idx] = ismember(levels, levels); %#ok % identity map + [ok_ref, ref_idx] = ismember(data_ref(:).', levels); + if ~all(ok_ref) + warning('Some data_ref symbols are not in "levels". True-path build may fail.'); + end + + % Precompute next_state(from_state, u_idx) LUT such that: + % combs(next_state,:) == [ combs(from_state,2:end) , levels(u_idx) ] + next_state = zeros(size(combs,1), numel(levels), 'uint32'); + for from = 1:size(combs,1) + prefix = combs(from,2:end); % what must match in 'to' for a valid transition + for ui = 1:numel(levels) + target = [prefix, levels(ui)]; + % find the unique 'to' whose state vector equals target + to = find(all(bsxfun(@eq, combs, target), 2), 1, 'first'); + if isempty(to), to = 0; end + next_state(from, ui) = to; + end + end + + % Initialize true path at n=1: pick any state with last_sym == data_ref(1) + % Prefer one whose suffix matches the first available history if L>1. + cand = find(last_sym == data_ref(1)); + if isempty(cand) + % fallback: choose closest in amplitude (should not happen if levels match) + [~,ix] = min(abs(last_sym - data_ref(1))); + cand = ix; + end + true_state_path = zeros(1,N,'uint32'); + true_state_path(1) = cand(1); + + % Propagate forward using the known inputs data_ref(n) + for n = 2:N + ui = ref_idx(n); % index of the actual transmitted level at time n + from = true_state_path(n-1); + if from==0 || ui==0 + true_state_path(n) = 0; + else + true_state_path(n) = next_state(from, ui); + if true_state_path(n)==0 + % Safety fallback: if no valid transition found (should not happen) + % choose any to-state whose vector matches shift+current symbol + target = [combs(from,2:end), levels(ui)]; + to = find(all(bsxfun(@eq, combs, target), 2), 1, 'first'); + if isempty(to), to = from; end + true_state_path(n) = to; + end + end + end + + %% ----- Collect branch metrics along decoded vs. true path ----- + bm_decoded = nan(1,N); + bm_true = nan(1,N); + + % n=1 in your code stores pm into bm_fw(:,:,1); real BMs start at n>=2 + for n = 2:N + % Decoded path: to = viterbi_path(n), from = survivor that fed it + to_d = viterbi_path(n); + from_d = pm_survivor_fw_idx(to_d, n); + bm_decoded(n) = bm_fw(to_d, from_d, n); + + % True path: transition true_state_path(n-1) -> true_state_path(n) + to_t = true_state_path(n); + from_t = true_state_path(n-1); + if to_t>0 && from_t>0 + bm_true(n) = bm_fw(to_t, from_t, n); + end + end + + % Convert to "cost" for intuitive plotting (your BM is a log-likelihood) + cost_dec = -bm_decoded; + cost_true= -bm_true; + + %% ----- Plot a short window for clarity ----- + win = max(2, N-20000):N; % last 200 samples (adjust as needed) + figure('Color','w'); hold on; grid on; box on; + plot(win, cost_true(win), 'LineWidth',1.2, 'DisplayName','True path cost (−BM)'); + plot(win, cost_dec(win), 'LineWidth',1.2, 'DisplayName','Decoded path cost (−BM)'); + xlabel('Time index n'); ylabel('Branch cost'); title('Branch metrics along true vs decoded path'); + legend('Location','best'); + + %% ----- Optional: overlay symbol levels for the same window ----- + yyaxis right + plot(win, data_in(win), ':', 'LineWidth',0.8, 'DisplayName','y(n)'); + ylabel('Amplitude'); + end VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path); @@ -544,11 +657,6 @@ classdef MLSE < handle end - function s = logsumexp(a,dim) - % returns log(sum(exp(a),dim)) safely - amax = max(a,[],dim); - s = amax + log(sum(exp(a - amax), dim)); - end end end diff --git a/Classes/05_Lab/Exfo_laser.m b/Classes/05_Lab/Exfo_laser.m index c2433cf..f70ada0 100644 --- a/Classes/05_Lab/Exfo_laser.m +++ b/Classes/05_Lab/Exfo_laser.m @@ -12,6 +12,7 @@ classdef Exfo_laser < handle safety_mode end + %% Any user can call these methods to alter the laser state methods (Access=public) function obj = Exfo_laser(options) @@ -169,6 +170,10 @@ classdef Exfo_laser < handle end end %public mehtods + + + % DAU must not be able to call these funcitons outside of the class! + % Hence they are private! :-) methods (Access=private) function v = connectLaser_(obj) @@ -180,7 +185,7 @@ classdef Exfo_laser < handle configureTerminator(v, "CR", "CR"); % Set the terminator to carriage return (CR) elseif obj.lab_interface == lab_interface.gpib % Connect via GPIB adress - visastring = ['GPIB0::',obj.connection_id,'::INSTR']; + visastring = ['GPIB1::',obj.connection_id,'::INSTR']; v = visadev(visastring); else error('Interface not supported'); @@ -342,8 +347,13 @@ classdef Exfo_laser < handle command = ['CH', num2str(channel), ':L=', num2str(desiredWavelength_nm)]; writeline(serialObj, command); + if abs(obj.cur_wavelength - desiredWavelength_nm)>30 + pause(5); %it takes time to tune the laser! If this is not here it WILL break! + else + pause(1.5); + end if serialObj.Type == visalib.InterfaceType.serial - pause(0.2); + pause(1.5); response = readline(serialObj); success = contains(response, 'OK'); end diff --git a/Classes/05_Lab/LabDeviceTemplate.m b/Classes/05_Lab/LabDeviceTemplate.m new file mode 100644 index 0000000..af613e5 --- /dev/null +++ b/Classes/05_Lab/LabDeviceTemplate.m @@ -0,0 +1,83 @@ +classdef LabDeviceTemplate < handle + + % Minimal template for lab device control via VISA / SCPI + % Copy this file and adapt: + % - visaAddress + % - public API methods + % - private SCPI commands + + properties (Access = public) + active logical = true + end + + properties (Access = protected) + visaAddress string + end + + methods (Access = public) + + function obj = LabDeviceTemplate(options) + arguments + options.active logical = true + options.visaAddress string = "" + end + + % apply options + fn = fieldnames(options); + for k = 1:numel(fn) + obj.(fn{k}) = options.(fn{k}); + end + + % optional: quick connectivity check + if obj.active + v = obj.connect(); + writeline(v,"*IDN?"); + idn = readline(v); + disp("Connected to: " + string(idn)); + delete(v); + end + end + + + function success = set(obj, value) + % Example public API + v = obj.connect(); + success = obj.setValue_(v, value); + delete(v); + end + + + function value = read(obj) + % Example readback + v = obj.connect(); + value = obj.readValue_(v); + delete(v); + end + + end + + + methods (Access = protected) + + function v = connect(obj) + assert(obj.visaAddress ~= "", "No VISA address defined"); + v = visadev(obj.visaAddress); + end + + end + + + methods (Access = private) + + function success = setValue_(~, v, value) + writeline(v, sprintf("SET %g", value)); + success = true; + end + + function value = readValue_(~, v) + writeline(v, "READ?"); + value = str2double(readline(v)); + end + + end +end diff --git a/Classes/05_Lab/OSA_Advantest.m b/Classes/05_Lab/OSA_Advantest.m new file mode 100644 index 0000000..3eb2a22 --- /dev/null +++ b/Classes/05_Lab/OSA_Advantest.m @@ -0,0 +1,327 @@ +classdef OSA_Advantest < handle + % Advantest Q8384 Optical Spectrum Analyzer (GPIB) + % User inputs in nanometers + + % EXAMPLE: + + % span_nm = 70; + % lambda_c_nm = 1310; + % osa = OSA_Advantest( ... + % 'span_nm', span_nm, ... + % 'lambda_c_nm', lambda_c_nm, ... + % 'resolution_nm', 0.1, ... + % 'sampling_points', 10001 ); + % + % osa.configure(); + % [lambda_nm, psd_dBm] = osa.measure(); + % figure() + % plot(lambda_nm,psd_dBm); + + properties (Access = public) + span_nm double = 5 + resolution_nm double = 0.05 + lambda_c_nm double = 1550 + sampling_points double = 1001 + timeout_s double = 10 + + lambda_nm + psd_dBm + end + + properties (Constant) + valid_resolutions_nm = [0.01 0.02 0.05 0.10 0.20 0.50] + valid_sampling_points = [101 201 501 1001 2001 5001 10001]; + end + + properties (Access = protected) + visaAddress string = "GPIB0::3::INSTR" + end + + methods + function obj = OSA_Advantest(options) + arguments + options.visaAddress string = "GPIB0::3::INSTR" + options.span_nm double = 5 + options.resolution_nm double = 0.05 + options.lambda_c_nm double = 1550 + options.sampling_points double = 1001 + options.timeout_s double = 10 + end + + fn = fieldnames(options); + for k = 1:numel(fn) + obj.(fn{k}) = options.(fn{k}); + end + + assert(ismember(obj.resolution_nm, obj.valid_resolutions_nm), ... + 'Invalid resolution. Allowed: 0.01 | 0.02 | 0.05 | 0.10 | 0.20 | 0.50 nm'); + assert(ismember(obj.sampling_points, obj.valid_sampling_points), ... + 'Invalid # of Samp. Points. Allowed: 101 201 501 1001 2001 5001 10001'); + + v = obj.connect(); + writeline(v,"*IDN?"); + disp(readline(v)); + delete(v); + end + + function configure(obj) + assert(ismember(obj.resolution_nm, obj.valid_resolutions_nm), ... + 'Invalid resolution. Allowed: 0.01 | 0.02 | 0.05 | 0.10 | 0.20 | 0.50 nm'); + assert(ismember(obj.sampling_points, obj.valid_sampling_points), ... + 'Invalid # of Samp. Points. Allowed: 101 201 501 1001 2001 5001 10001'); + + v = obj.connect(); + + writeline(v,"FMT0,HED0,SDL0"); + writeline(v,"BUZ0"); + writeline(v,"QUI0"); + + writeline(v, sprintf("RES %gnm", obj.resolution_nm)); + writeline(v, sprintf("SPT %g", obj.sampling_points)); + writeline(v, sprintf("CEN %gnm", obj.lambda_c_nm)); + writeline(v, sprintf("SPA %gnm", obj.span_nm)); + writeline(v,"SWE 0"); + + delete(v); + end + + function [osa_x_nm, osa_y_dBm] = measure(obj) + v = obj.connect(); + + writeline(v,"CSB"); + writeline(v,"MEA1"); + + active = 1; cnt = 0; + while active + pause(1); + writeline(v,"MEA?"); + active = str2double(readline(v)); + cnt = cnt + 1; + if cnt > obj.timeout_s + delete(v); + error("OSA_Q8384:Timeout","Measurement timeout"); + end + end + + writeline(v,"PKL"); + + writeline(v,"ODN?"); + readline(v); % number of points (not strictly needed) + + writeline(v,"FMT0,HED0,SDL0"); + + writeline(v,"OSD0"); % Y-axis + osa_y_dBm = str2num(readline(v)); + + writeline(v,"OSD1"); % X-axis + osa_x_m = str2num(readline(v)); + osa_x_nm = osa_x_m.*1e9; + delete(v); + + obj.lambda_nm=osa_x_nm; + obj.psd_dBm=osa_y_dBm; + + end + + % --- Replace / extend plot() in OSA_Q8384 class --- + + function plot(obj, options) + arguments + obj + options.FigureNumber double = 1 + options.Hold logical = true + options.Title string = "OSA Spectrum" + options.LineWidth double = 0.8 + options.Grid logical = true + options.Color (1,3) double = [0 0.4470 0.7410] + options.DisplayName string = "" + end + + if isempty(obj.psd_dBm) || isempty(obj.lambda_nm) + error('Record spectrum first: run osa.measure()'); + end + + fig = figure(options.FigureNumber); + fig.Color = 'w'; + + ax = gca; + if ~options.Hold + cla(ax); + else + hold(ax,'on'); + end + + plot(ax, obj.lambda_nm, obj.psd_dBm, ... + 'LineWidth', options.LineWidth, ... + 'Color', options.Color, ... + 'DisplayName', options.DisplayName); + + xlabel(ax,'Wavelength [nm]'); + ylabel(ax,'Optical Power [dBm]'); + + if options.Title ~= "" + title(ax, options.Title); + end + + xlim(ax,[min(obj.lambda_nm) max(obj.lambda_nm)]); + yl = ylim(ax); + ylim(ax,[yl(1)-1 yl(2)+1]); + + if options.Grid + grid(ax,'on'); + grid(ax,'minor'); + end + + set(ax, ... + 'FontSize',11, ... + 'LineWidth',1, ... + 'Box','on'); + + if options.DisplayName ~= "" + legend(ax,'show','Location','best'); + end + end + + function osnr = computeOSNR(obj, options) + % OSNR computation based on ratio method (RBW-independent) + % Assumes obj.lambda_nm [nm], obj.psd_dBm [dBm/RBW] exist + + arguments + obj + options.bw_signal_nm double = 0.1 % channel bandwidth (0.1 for CW?!?!) + options.ref_bw_nm double = 0.1 % OSNR reference bandwidth + options.carrier_frequency = NaN; + options.noise_guard_nm = 1; + options.noise_window_nm =1; + options.debugplots = 0; + end + + assert(~isempty(obj.lambda_nm) && ~isempty(obj.psd_dBm), ... + 'Run measure() before computing OSNR'); + + obj.lambda_nm = obj.lambda_nm; + psd = obj.psd_dBm; + + % --- find carrier wavelength recorded in OSA --- + % options.carrier_frequency [nm], options.noise_guard_nm, options.noise_window_nm + + if ~isnan(options.carrier_frequency) + + % local search window around expected CW carrier + idx_search = obj.lambda_nm > (options.carrier_frequency - options.noise_guard_nm) & ... + obj.lambda_nm < (options.carrier_frequency + options.noise_guard_nm); + + [~, imax_local] = max(psd(idx_search)); + lambda_search = obj.lambda_nm(idx_search); + lambda_c = lambda_search(imax_local); + [~,imax] = find(obj.lambda_nm==lambda_c); + + % define local noise windows directly adjacent to signal (guarded) + idx_noise_left = obj.lambda_nm > (lambda_c - options.noise_guard_nm - options.noise_window_nm) & ... + obj.lambda_nm < (lambda_c - options.noise_guard_nm); + + idx_noise_right = obj.lambda_nm > (lambda_c + options.noise_guard_nm) & ... + obj.lambda_nm < (lambda_c + options.noise_guard_nm + options.noise_window_nm); + + idx_noise = idx_noise_left | idx_noise_right; + + else + % fallback: global maximum (unsafe for pathological ASE cases) + [~, imax] = max(psd); + lambda_c = obj.lambda_nm(imax); + + % ASE from outer quarters of spectrum + N = numel(obj.lambda_nm); + idx_noise = false(size(obj.lambda_nm)); + idx_noise([3:round(N/4), round(3*N/4):N-1]) = true; + end + + % --- extract signal window --- + idx_signal = obj.lambda_nm > (lambda_c - options.bw_signal_nm/2) & ... + obj.lambda_nm < (lambda_c + options.bw_signal_nm/2); + + if ~any(idx_signal) + idx_signal(imax) = true; + end + + cc_lambda = obj.lambda_nm(idx_signal); + cc_psd = psd(idx_signal); + + % --- estimate ASE from local noise (flat ASE assumption) --- + noise_lambda = obj.lambda_nm(idx_noise); + noise_psd = psd(idx_noise); + + + ase_psd = interp1(noise_lambda, noise_psd, cc_lambda, ... + 'linear', 'extrap'); + + % --- convert to linear (mW / RBW) --- + sig_lin = 10.^(cc_psd/10); + ase_lin = 10.^(ase_psd/10); + + % --- integrate (RBW cancels) --- + P_sig_lin = sum(sig_lin); + P_ase_lin = sum(ase_lin); + + P_sig_dB = 10*log10(P_sig_lin); + P_ase_dB = 10*log10(P_ase_lin); + + osnr.p_sig_db = P_sig_dB; + osnr.p_ase_db = P_ase_dB; + + % --- OSNR direct from peak value to the noise estimate out of band --- + osnr.direct_dB = psd(imax) - 10*log10(mean(ase_lin)) ; + + % --- OSNR in measurement bandwidth --- + osnr.measured_dB = P_sig_dB - P_ase_dB; + + % --- normalize to reference bandwidth (usually 0.1 nm) --- + osnr.ref_dB = osnr.measured_dB + ... + 10*log10(options.bw_signal_nm / options.ref_bw_nm); + + % --- ASE-corrected OSNR (optional definition) --- + osnr.corrected_dB = ... + 10*log10(10^(P_sig_dB/10) - 10^(P_ase_dB/10)) - P_ase_dB; + + osnr.corrected_ref_dB = osnr.corrected_dB + ... + 10*log10(options.bw_signal_nm / options.ref_bw_nm); + + % metadata + osnr.lambda_c_nm = lambda_c; + osnr.bw_signal_nm = options.bw_signal_nm; + osnr.ref_bw_nm = options.ref_bw_nm; + + if options.debugplots + figure(100) + xline(lambda_c,'LineWidth',2,'DisplayName','Max. OSA Value','Color',[0.8,0.8,0.8]) + obj.plot("DisplayName",'OSNR','FigureNumber',100); + yline(osnr.p_ase_db,'LineWidth',2,'DisplayName','Signal Estimation'); + yline(osnr.p_sig_db,'LineWidth',2,'DisplayName','Noise Estimation'); + scatter(obj.lambda_nm(idx_noise),obj.psd_dBm(idx_noise),'Marker','.','LineWidth',4,'DisplayName','Noise Samples','MarkerEdgeColor','red'); + scatter(obj.lambda_nm(idx_signal),obj.psd_dBm(idx_signal),'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','green'); + scatter(obj.lambda_nm(idx_signal),ase_psd,'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','magenta'); + ylim([min(obj.psd_dBm), max([obj.psd_dBm,osnr.p_sig_db+5])]); + + figure(101);hold on + xline(lambda_c,'LineWidth',2,'DisplayName','Max. OSA Value','Color',[0.8,0.8,0.8]) + xline(lambda_c - options.bw_signal_nm/2,'LineWidth',2,'DisplayName','CW - 0.5 BW','Color',[0.6,0.6,0.8]) + xline(lambda_c + options.bw_signal_nm/2,'LineWidth',2,'DisplayName','CW + 0.5 BW','Color',[0.6,0.8,0.8]) + obj.plot("DisplayName",'OSNR','FigureNumber',101); + yline(osnr.p_ase_db,'LineWidth',2,'DisplayName','Signal Estimation'); + yline(osnr.p_sig_db,'LineWidth',2,'DisplayName','Noise Estimation'); + scatter(obj.lambda_nm(idx_signal),obj.psd_dBm(idx_signal),'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','green'); + scatter(obj.lambda_nm(idx_signal),ase_psd,'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','magenta'); + xlim([lambda_c - options.bw_signal_nm, lambda_c + options.bw_signal_nm]) + ylim([min(obj.psd_dBm), max([obj.psd_dBm,osnr.p_sig_db+5])]); + end + + end + + end + + methods (Access = protected) + function v = connect(obj) + v = visadev(obj.visaAddress); + end + end +end diff --git a/Classes/05_Lab/OptLaserN7714A.m b/Classes/05_Lab/OptLaserN7714A.m new file mode 100644 index 0000000..b6a5372 --- /dev/null +++ b/Classes/05_Lab/OptLaserN7714A.m @@ -0,0 +1,146 @@ +classdef OptLaserN7714A < handle + % Minimal controller for Keysight N7714A tunable laser over VISA-LAN + % Methods: getLaserInfo, setPower, setWavelength + % + % Examples: + % L = OptLaserN7714A('TCPIP::192.168.0.50::INSTR'); + % L.setWavelength(1550); % nm + % L.setPower(-3.0, true); % dBm, also turns output ON + % info = L.getLaserInfo(); + + properties (Access=private) + v % visadev handle + resource char % VISA resource string, e.g. 'TCPIP::...::INSTR' + end + + methods + function obj = OptLaserN7714A(resource) + arguments + resource (1,:) char + end + obj.resource = resource; + + % Robust connect with small retry loop + maxRetries = 3; pauseTime = 1.0; lastErr = []; + for k = 1:maxRetries + try + obj.v = visadev(obj.resource); + % Identify instrument (SCPI *IDN?) + writeline(obj.v, '*IDN?'); % SCPI common, identification + idn = strtrim(readline(obj.v)); %#ok + break + catch ME + lastErr = ME; + pause(pauseTime); + end + end + if isempty(obj.v) || ~isvalid(obj.v) + error('OptLaserN7714A:ConnectFailed', ... + 'Could not connect to %s. Last error: %s', obj.resource, lastErr.message); + end + + % Set power unit to dBm for SOURce tree to be explicit (optional) + % [:SOUR]:POWer:UNIT dBm (laser power unit) + % Ref: SOURce:POWer:UNIT. :contentReference[oaicite:0]{index=0} + try + writeline(obj.v, 'SOUR:POW:UNIT DBM'); + catch + % non-fatal + end + end + + function delete(obj) + if ~isempty(obj.v) && isvalid(obj.v); delete(obj.v); end + end + + function setPower(obj, power_dBm, outputOn, channel) + arguments + obj + power_dBm (1,1) double + outputOn (1,1) logical = true + channel (1,1) double {mustBeMember(channel,0:4)} = 1 + end + + v = obj.v; + cmd = sprintf('SOUR%d:POW:LEV:IMM:AMPL %g DBM', channel, power_dBm); + writeline(v, cmd); + + if outputOn + writeline(v, sprintf('SOUR%d:POW:STAT 1', channel)); %SOUR1:POW:STAT 0 + else + writeline(v, sprintf('SOUR%d:POW:STAT 0', channel)); + end + + writeline(v, '*OPC?'); + a=readline(v); + end + + function setWavelength(obj, wavelength_nm, channel) + arguments + obj + wavelength_nm (1,1) double {mustBePositive} + channel (1,1) double {mustBeMember(channel,0:4)} = 1 + end + v = obj.v; + cmd = sprintf('SOUR%d:WAV %gNM', channel, wavelength_nm); + writeline(v, cmd); + writeline(v, '*OPC?'); readline(v); + end + + function setDither(obj, enable, port) + % Enable or disable laser frequency dither (stabilization). + % enable = false → no dither (FIX), true → stabilization (STAB). + % port = 1..4 (default 1). + if nargin < 3 + port = 1; + end + + v = obj.v; + if enable + writeline(v, sprintf('SOUR%d:CONTrol:FCDither ON', port)); %[:SOURce[n]][:CHANnel[m]]:CONTrol:FCDither + else + writeline(v, sprintf('SOUR%d:CONTrol:FCDither OFF', port)); + end + + % optional confirmation + writeline(v, sprintf('SOUR%d:CONTrol:FCDither?', port)); + modeStr = strtrim(readline(v)); + fprintf('Port %d dither mode set to %s\n', port, modeStr); + end + + + + function info = getLaserInfo(obj, port) + % Returns struct with IDN, output state, wavelength (nm), power (dBm) + % port = 1..4 (for N7714A with 4 outputs). Default = 1. + if nargin < 2 + port = 1; + end + + v = obj.v; + + % *IDN? + writeline(v, '*IDN?'); + idn = strtrim(readline(v)); + + % Output state: :SOURn:POW:STAT? + writeline(v, sprintf('SOUR%d:POW:STAT?', port)); + out_state = str2double(readline(v)) ~= 0; + + % Wavelength query (returns meters → convert to nm) + writeline(v, sprintf('SOUR%d:WAV?', port)); + wav_m = str2double(readline(v)); + wav_nm = wav_m * 1e9; + + % Power (set level) query (dBm) + writeline(v, sprintf('SOUR%d:POW:LEV:IMM:AMPL?', port)); + p_dBm = str2double(readline(v)); + + info = struct('idn', idn, ... + 'output_on', out_state, ... + 'wavelength_nm', wav_nm, ... + 'power_dBm', p_dBm); + end + + end +end diff --git a/Classes/05_Lab/OptPowerMeter8153A.m b/Classes/05_Lab/OptPowerMeter8153A.m new file mode 100644 index 0000000..a815f8c --- /dev/null +++ b/Classes/05_Lab/OptPowerMeter8153A.m @@ -0,0 +1,168 @@ +classdef OptPowerMeter8153A < handle + + % Code for HEWLETT-PACKARD 8153A Optical Power Meter (2-channel). + % https://www.artisantg.com/info/Agilent_8153A_Manual.pdf?srsltid=AfmBOoqTe38sbGRNHUFXjw5uO6Br7I7aSfgAdRpx-hVvM4sTIhFYyD5x + % PAGE 166++ + + % Typical usage: + % pm = HPPowerMeter8153A( ... + % 'active', [false true], ... + % 'wavelength_nm', [1550 1310] ); + % + % pm.setWavelength([1550 1310]); % only CH2 is modified + % p = pm.readPower(); % reads only active channels + + + properties (Access = public) + active logical = [true true] % channel enable + wavelength_nm double = [1550 1550] % per-channel wavelength [nm] + power_dBm double = [NaN NaN] % last read power + offset_dB double = [0 0] % per-channel power offset [dB] + avgTime_s double = [0.2 0.2]; + end + + properties (Constant) + avgTime_min_s = 0.02 % 20 ms + avgTime_max_s = 3600 % 1 hour + end + + properties (Access = protected) + visaAddress string = "GPIB1::22::INSTR" + numChannels double = 2 + end + + methods + function obj = OptPowerMeter8153A(options) + arguments + options.visaAddress string = "GPIB1::22::INSTR" + options.active logical = [true true] + options.wavelength_nm double = [1550 1550] + end + + fn = fieldnames(options); + for k = 1:numel(fn) + obj.(fn{k}) = options.(fn{k}); + end + + assert(numel(obj.active)==obj.numChannels) + assert(numel(obj.wavelength_nm)==obj.numChannels) + + v = obj.connect(); + writeline(v,"*IDN?"); + disp(readline(v)); + pause(0.1); + delete(v); + pause(0.1); + + obj.setWavelength(obj.wavelength_nm); + end + + function setWavelength(obj, lambda_nm) + if nargin > 1 + obj.wavelength_nm = lambda_nm; + end + + v = obj.connect(); + for ch = 1:obj.numChannels + if obj.active(ch) + writeline(v, ... + sprintf("SENS%d:POW:WAVE %gNM", ch, obj.wavelength_nm(ch))); + end + end + pause(0.1); + delete(v); + pause(0.1); + end + + function p = readPower(obj) + v = obj.connect(); + for ch = 1:obj.numChannels + if obj.active(ch) + writeline(v, sprintf("READ%d:POW?", ch)); + obj.power_dBm(ch) = str2double(readline(v)); + else + obj.power_dBm(ch) = NaN; + end + end + delete(v); + p = obj.power_dBm; + end + + function setOffsetCorrection(obj, offset_dB) + obj.offset_dB = offset_dB; + + v = obj.connect(); + for ch = 1:obj.numChannels + if obj.active(ch) + writeline(v, ... + sprintf("SENS%d:CORR:LOSS:INP:MAGN %gDB", ch, obj.offset_dB(ch))); + end + end + delete(v); + end + + function offset_dB = getOffsetCorrection(obj) + v = obj.connect(); + offset_dB = NaN(1,obj.numChannels); + for ch = 1:obj.numChannels + if obj.active(ch) + writeline(v, sprintf("SENS%d:CORR:LOSS:INP:MAGN?", ch)); + offset_dB(ch) = str2double(readline(v)); + end + end + delete(v); + obj.offset_dB = offset_dB; + end + + function setAvgTime(obj, avgTime_s) + + if any(avgTime_s < obj.avgTime_min_s) || any(avgTime_s > obj.avgTime_max_s) + + oldv = avgTime_s; + + avgTime_s = max(avgTime_s, obj.avgTime_min_s); + avgTime_s = min(avgTime_s, obj.avgTime_max_s); + + fprintf('[HPPowerMeter8153A] Averaging time clipped:\n'); + for ch = 1:obj.numChannels + if oldv(ch) ~= avgTime_s(ch) + fprintf(' CH%d: %.3g s → %.3g s (allowed %.3g … %.3g s)\n', ... + ch, oldv(ch), avgTime_s(ch), ... + obj.avgTime_min_s, obj.avgTime_max_s); + end + end + end + + obj.avgTime_s = avgTime_s; + + v = obj.connect(); + for ch = 1:obj.numChannels + if obj.active(ch) && ~isnan(obj.avgTime_s(ch)) + writeline(v, ... + sprintf("SENS%d:POW:ATIME %g", ch, obj.avgTime_s(ch))); + end + end + delete(v); + end + + function avgTime_s = getAvgTime(obj) + v = obj.connect(); + avgTime_s = NaN(1,obj.numChannels); + for ch = 1:obj.numChannels + if obj.active(ch) + writeline(v, sprintf("SENS%d:POW:ATIME?", ch)); + avgTime_s(ch) = str2double(readline(v)); + end + end + delete(v); + obj.avgTime_s = avgTime_s; + end + + end + + methods (Access = protected) + function v = connect(obj) + v = visadev(obj.visaAddress); + end + end +end diff --git a/Classes/05_Lab/Thor_PDFA.m b/Classes/05_Lab/Thor_PDFA.m index fefad8f..9f0a88a 100644 --- a/Classes/05_Lab/Thor_PDFA.m +++ b/Classes/05_Lab/Thor_PDFA.m @@ -20,7 +20,7 @@ classdef Thor_PDFA < handle function obj = Thor_PDFA(options) arguments - options.serialport_number = 'COM12' + options.serialport_number = 'COM18' options.safety_mode = 1; end @@ -37,7 +37,7 @@ classdef Thor_PDFA < handle end function o = connectSerial(obj) - + o = []; try % Connect to the PDFA o = serialport(obj.serialport_number, 9600); @@ -62,11 +62,15 @@ classdef Thor_PDFA < handle function getStatus(obj) o = obj.connectSerial(); - obj.getStatus_(o); - if obj.safety_mode - obj + + if ~isempty(o) + obj.getStatus_(o); + if obj.safety_mode + obj + end end + end function enablePDFA(obj) diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index f1ca74e..0cb5245 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -51,7 +51,7 @@ classdef DBHandler < handle "Vendor", "MySQL", ... "Server", "134.245.243.254", ... "PortNumber", 3306, ... - "JDBCDriverLocation", "C:\Users\Silas\Documents\mysql-connector-j-9.3.0\mysql-connector-j-9.3.0.jar"); + "JDBCDriverLocation", "C:\Users\sioe\Documents\mysql-connector-j-9.3.0\mysql-connector-j-9.3.0.jar"); end catch e @@ -588,14 +588,13 @@ classdef DBHandler < handle return catch ME if attempt < maxFast - pause(1) + pause(0.1) else - pause(10) + pause(1) end lastErr = ME; end end - pause(60) error('Database fetch failed after %d attempts:\n%s', maxSlow, lastErr.getReport()) end diff --git a/Classes/DataBaseHandler/Metricstruct.m b/Classes/DataBaseHandler/Metricstruct.m index 403e90c..ef00675 100644 --- a/Classes/DataBaseHandler/Metricstruct.m +++ b/Classes/DataBaseHandler/Metricstruct.m @@ -16,9 +16,9 @@ classdef Metricstruct SNR (1,1) double {mustBeNumeric} = NaN SNR_level (:,1) double {mustBeNumeric} = [] STD (1,1) double {mustBeNumeric} = NaN - STD_level (:,1) double {mustBeNumeric, mustBeNonnegative} = [] + STD_level (:,1) double = [] STDrx (1,1) double {mustBeNumeric} = NaN - STDrx_level (:,1) double {mustBeNumeric, mustBeNonnegative} = [] + STDrx_level (:,1) double = [] EVM (1,1) double {mustBeNumeric} = NaN EVM_level (:,1) double {mustBeNumeric} = [] @@ -71,16 +71,22 @@ classdef Metricstruct end end - function print(obj) + function print(obj,options) % Print method to display key metrics in a formatted way + arguments + obj + options.description = ''; + end + % Define the width for formatting nameWidth = 15; % Width for parameter names valueWidth = 12; % Width for values % Print header + fprintf('\n%s\n', repmat('=', 1, nameWidth + valueWidth)); - fprintf(' Results \n'); + fprintf([char(options.description),' Results \n']); fprintf('%s\n', repmat('=', 1, nameWidth + valueWidth)); % Function to format numbers with appropriate precision diff --git a/Classes/GifWriter.m b/Classes/GifWriter.m new file mode 100644 index 0000000..c6483db --- /dev/null +++ b/Classes/GifWriter.m @@ -0,0 +1,146 @@ +classdef GifWriter < handle + %GIFWRITER Simple class to create GIFs from figures (parallel-safe) + % + % Example: + % g = GifWriter('Name','mySim','Parallel',true); + % parfor i = 1:10 + % plot(rand(10,1)); + % g.addFrame(1,i); + % end + % g.compile(1); + + properties + Name (1,:) char = 'default' % GIF base name + DelayTime (1,1) double = 0.1 % Frame delay in seconds + Parallel (1,1) logical = false % Enable parallel-safe mode + BaseDir (1,:) char % Base directory for temp frames + OutputDir (1,:) char % Final GIF output directory + end + + methods + %% Constructor + function obj = GifWriter(varargin) + % Parse name/value pairs + p = inputParser; + addParameter(p, 'Name', 'default', @ischar); + addParameter(p, 'DelayTime', 0.1, @isnumeric); + addParameter(p, 'Parallel', false, @islogical); + addParameter(p, 'OutputDir', fullfile(pwd, 'gif_output'), @ischar); + parse(p, varargin{:}); + + obj.Name = p.Results.Name; + obj.DelayTime = p.Results.DelayTime; + obj.Parallel = p.Results.Parallel; + obj.OutputDir = p.Results.OutputDir; + obj.BaseDir = fullfile(obj.OutputDir, 'tmp', obj.Name); + + if ~exist(obj.BaseDir, 'dir'), mkdir(obj.BaseDir); end + if ~exist(obj.OutputDir, 'dir'), mkdir(obj.OutputDir); end + end + + %% + function addFrame(obj, figInput, pos) + %ADDFRAME Add a figure frame to the GIF (supports parallel mode) + % + % Usage: + % obj.addFrame(figHandle) + % obj.addFrame(figNum) + % obj.addFrame(figHandle, pos) % parallel mode + % obj.addFrame(figNum, pos) + % + % In parallel mode, 'pos' must be a unique integer (loop index). + + if nargin < 3, pos = []; end + + % --- Resolve figure handle --- + if isnumeric(figInput) + % User passed a figure number + if ~ishandle(figInput) + warning('GifWriter:addFrame', 'Figure %d not found.', figInput); + return; + end + figHandle = figure(figInput); + elseif isa(figInput, 'matlab.ui.Figure') + figHandle = figInput; + else + error('GifWriter:addFrame:InvalidInput', ... + 'Input must be a figure handle or figure number.'); + end + + % --- Parallel-safe frame writing --- + if obj.Parallel + if isempty(pos) + error('GifWriter:ParallelMode', ... + 'In parallel mode, provide a unique ''pos'' identifier.'); + end + + % Directory for this figure number + frameDir = fullfile(obj.BaseDir, sprintf('fig_%d', figHandle.Number)); + if ~exist(frameDir, 'dir') + mkdir(frameDir); + end + + % File path for this frame + frameFile = fullfile(frameDir, sprintf('frame_%05d.png', pos)); + + % Export to PNG (headless-safe) + exportgraphics(figHandle, frameFile, 'Resolution', 150); + + else + % --- Serial mode: append directly to GIF --- + gifFile = fullfile(obj.OutputDir, ... + sprintf('%s_fig_%d.gif', obj.Name, figHandle.Number)); + + % Export frame temporarily + tmpFile = [tempname, '.png']; + exportgraphics(figHandle, tmpFile, 'Resolution', 150); + img = imread(tmpFile); + delete(tmpFile); + + % Append to GIF + [A, map] = rgb2ind(img, 256); + if ~isfile(gifFile) + imwrite(A, map, gifFile, 'gif', ... + 'LoopCount', Inf, 'DelayTime', obj.DelayTime); + else + imwrite(A, map, gifFile, 'gif', ... + 'WriteMode', 'append', 'DelayTime', obj.DelayTime); + end + end + end + + + + %% Compile all PNGs into a GIF (and clean up) + function compile(obj, fignum) + figDir = fullfile(obj.BaseDir, sprintf('fig_%d', fignum)); + gifFile = fullfile(obj.OutputDir, sprintf('%s_fig_%d.gif', obj.Name, fignum)); + + frames = dir(fullfile(figDir, 'frame_*.png')); + if isempty(frames) + warning('GifWriter:NoFrames', 'No frames found for figure %d.', fignum); + return; + end + + % Sort by frame name + [~, idx] = sort({frames.name}); + frames = frames(idx); + + % Combine into a GIF + for i = 1:numel(frames) + img = imread(fullfile(frames(i).folder, frames(i).name)); + [A, map] = rgb2ind(img, 256); + if i == 1 + imwrite(A, map, gifFile, 'gif', ... + 'LoopCount', Inf, 'DelayTime', obj.DelayTime); + else + imwrite(A, map, gifFile, 'gif', ... + 'WriteMode', 'append', 'DelayTime', obj.DelayTime); + end + end + + % Clean up temporary frames + rmdir(figDir, 's'); + end + end +end diff --git a/Classes/Warehouse_class/classes/DataStorage.m b/Classes/Warehouse_class/classes/DataStorage.m index 5fd288d..394a12f 100644 --- a/Classes/Warehouse_class/classes/DataStorage.m +++ b/Classes/Warehouse_class/classes/DataStorage.m @@ -137,7 +137,21 @@ classdef DataStorage < handle if ~isempty(tmp) if isa(tmp,'double') - value(i) = tmp ; + try + value(i,:) = tmp ; + catch + a = size(value,2); + b = size(tmp,2); + if a > b + value(i,:) =[tmp,NaN(1,a-b)] ; + elseif a < b + value(i,:) = tmp(1:size(value,2)) ; + else + error('unknwon case...') + end + + + end elseif isa(tmp,'Signal') || isa(tmp,'struct') || isa(tmp,'Exfo_laser') || isa(tmp,'DC_supply') if i == 1 value = {}; diff --git a/Classes/Warehouse_class/classes/Parameter.m b/Classes/Warehouse_class/classes/Parameter.m new file mode 100644 index 0000000..1673241 --- /dev/null +++ b/Classes/Warehouse_class/classes/Parameter.m @@ -0,0 +1,7 @@ +classdef Parameter < StorageParameter + methods + function obj = Parameter(varargin) + obj@StorageParameter(varargin{:}); + end + end +end diff --git a/Classes/Warehouse_class/functions/fwm_plots/CompleteRoutine.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/CompleteRoutine.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/CompleteRoutine.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/CompleteRoutine.m index 86cbe93..f55c95f 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/CompleteRoutine.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/CompleteRoutine.m @@ -1,251 +1,251 @@ -% Script, that shows the data management routine :-) - -loadExistingWareHouse = 0; - -if loadExistingWareHouse - - [file, path] = uigetfile(); - wh = load([path filesep file]); - wh = wh.wh; - wh.showInfo; - -else - - % 1) Define all your parameters, best practice directly constructs a - % structure - - params = struct; - - params.l = [2,10]; - - params.dispersion = [0]; - - params.sgm = [0]; - -% params.pol = ["YXYXYXYX","YXXYYXXY","YYYYYYYY"]; - params.pol = ["alternated","paired","copolarized"]; - - params.p_in = [3]; - - params.p_out = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2]; - - params.pmd = [0.1]; - - params.gamma = [0.0023]; - - params.realization = [1:20]; - - params.numchannels = [16]; - - params.center_wavelength = floor([getSweepWavelengths(35, 50e9, 1310)] .* 1000) ./ 1000 ; - params.center_wavelength = [1285 1287 1290 1292 1295]; - params.center_wavelength = 1310; - - params.channelspacing = [400e9]; - - params.random_zdw = [0]; - - %wh = warehouse :-) - wh = DataStorage(params); - - wh.showInfo; - - wh.addStorage("ber"); - - wh.addStorage("totalBer"); - -end - - - -%2) Simulate a bunch of data - TO BE IMPLEMENTED HERE - for now use scripts -%from Sebastian - -%3) Once the simulation folder is around, specifiy path and analyze dirs - -path = uigetdir('C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations'); - -allMat = getAllFilesInFolder(path,'.mat'); -allErr = getAllFilesInFolder(path,'.err'); - -%allMat = dir([path filesep '*.mat']); - -%allErr = dir([path filesep '*.err']); - -if numel(allMat) == 0 - warning('You defined an empty folder. Could not locate any .mat file.') -else - fprintf('%-20s', 'Err Files:'); fprintf('%-12s', num2str(numel(allErr))); fprintf('\n'); - fprintf('%-20s', 'Mat Files:'); fprintf('%-12s', num2str(numel(allMat))); fprintf('\n'); - fprintf('%-20s', 'Missing Mat Files:'); fprintf('%-12s', num2str(numel(allErr)-numel(allMat))); fprintf('\n'); -end - -%4) Now load that data - -f = waitbar(0,'Please wait...'); -cnt = 0; - -for num = 1:numel(allMat) - - fileName = allMat(num).name; - fileFolder = allMat(num).path; - fileExt = allMat(num).ext; -% - matFile = load([fileFolder filesep fileName fileExt]); - matFile = matFile.loop_data; - - % ____________________________________ - % FIND THE DATAPOINT CURRENTLY LOADED - zdw = 1310; - - channelplan = "symmetric"; - - channelspacing = str2double(strrep(regexp(fileName,'(_chsp)+([\d]*)','match'),'_chsp','')).*1e9; - - numchannels = str2double(strrep(regexp(fileName,'(ch)+(_)+([\d]*)','match'),'ch_','')); - - center_wavelength = str2double(insertAfter(strrep(regexp(fileName,'(lambda)+([\d]*)','match'),'lambda',''),4,'.')); - - center_wavelength = floor(center_wavelength * 1000) / 1000; - - if center_wavelength == 2192 - continue - end - - center_wavelength = 1310; - - random_zdw = str2double(strrep(regexp(fileName,'(rzwd)+([\d])','match'),'rzwd','')); - - l = str2double(strrep(regexp(fileName,'([L])+(_)+([\d]*)','match'),'L_','')); - - d = str2double(strrep(regexp(fileName,'([D])+(_)+([\d]*)','match'),'D_','')); - - if d == 0 - sgm = false; - else - sgm = true; - end - - - if numel(regexp(fileName,'(YYYY)','match')) > 1 - pol = "copolarized"; - elseif numel(regexp(fileName,'(YXXY)','match')) > 1 - pol = "paired"; - elseif numel(regexp(fileName,'(YXYX)','match')) > 1 - pol = "alternated"; - else - pol = "copolarized"; - end - - p_in = str2double(strrep(regexp(fileName,'(pow_)+([-,\d]{1})','match'),'pow_','')); - - pmd = 0.1; - - gamma = 0.0023; - - realiz = str2double(strrep(regexp(fileName,'(r)+([-,\d]{1,3})','match'),'r','')); - - - - % ____________________________________ - % Get the information you want from current file - rop=[]; - ber = []; - for pow = 2:12 - - module_number = ''; - for p = 1:11 %11 because there are 11 ROP branches in model - - % get ROP - if p == 1 - p_out = matFile.dp_optatten_para.atten; - else - p_out = matFile.("dp_optatten__"+(p)+"_para").atten; - end - - p_out = round(p_out-10*log10(numel(matFile.config.parameters.common.wavelengthPlan))); - - for c = 1:numel(matFile.config.parameters.common.wavelengthPlan) - - ber(c) = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,c}.ber; - - end - - totalBer = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,end}.totalBer; - - if totalBer > 0.2 && channelspacing == 400e9 && pol == "alternated" - disp("stopping here"); - pause; - end - - - % ____________________________________ - % Add value to warehouse at the correct position - - - - wh.addValueToStorage(ber,'ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw); - wh.getStoValue('ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw); - wh.addValueToStorage(totalBer,'totalBer',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels,center_wavelength,channelspacing,random_zdw); - - end - - end - waitbar(num/numel(allMat),f,'Loading your data'); -end - -close(f) - - - - - -% 4) Hey! the warehouse is here and (hopefully) filled with data :-) - -% Create a save dialog -defaultDir = 'C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\'; -defaultExt = '*.mat'; -[filename, pathname] = uiputfile(fullfile(defaultDir, defaultExt),'', 'wh.mat'); - -% Check if the user pressed Cancel -if isequal(filename, 0) || isequal(pathname, 0) - disp('Save operation canceled.'); -else - % Save the variable to the selected file - save(fullfile(pathname, filename), 'wh'); - disp(['Variable "wh" saved to: ', fullfile(pathname, filename)]); -end - - - - -function matFileStructArray = getAllFilesInFolder(folderPath,extension) - % Get a list of all files in the current folder - currentFolderFiles = dir(fullfile(folderPath, '*')); - - % Exclude '.' and '..' directories - currentFolderFiles = currentFolderFiles(~ismember({currentFolderFiles.name}, {'.', '..'})); - - % Initialize the structure array for .mat files - matFileStructArray = struct('path', {}, 'name', {}, 'ext', {}); - - % Loop over each file in the current folder - for i = 1:length(currentFolderFiles) - currentFile = currentFolderFiles(i); - - % Check if the current item is a file and has a .mat extension - if ~currentFile.isdir && endsWith(currentFile.name, extension, 'IgnoreCase', true) - % If it's a .mat file, add it to the structure array - [matFileStructArray(end + 1).path,matFileStructArray(end+1).name, matFileStructArray(end+1).ext] = fileparts(fullfile(folderPath, currentFile.name)); - elseif currentFile.isdir - % If it's a directory, recursively call the function - subfolderPath = fullfile(folderPath, currentFile.name); - subfolderMatFiles = getAllFilesInFolder(subfolderPath,extension); - - % Add .mat files from the subfolder to the structure array - matFileStructArray = [matFileStructArray, subfolderMatFiles]; - end - end -end - - +% Script, that shows the data management routine :-) + +loadExistingWareHouse = 0; + +if loadExistingWareHouse + + [file, path] = uigetfile(); + wh = load([path filesep file]); + wh = wh.wh; + wh.showInfo; + +else + + % 1) Define all your parameters, best practice directly constructs a + % structure + + params = struct; + + params.l = [2,10]; + + params.dispersion = [0]; + + params.sgm = [0]; + +% params.pol = ["YXYXYXYX","YXXYYXXY","YYYYYYYY"]; + params.pol = ["alternated","paired","copolarized"]; + + params.p_in = [3]; + + params.p_out = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2]; + + params.pmd = [0.1]; + + params.gamma = [0.0023]; + + params.realization = [1:20]; + + params.numchannels = [16]; + + params.center_wavelength = floor([getSweepWavelengths(35, 50e9, 1310)] .* 1000) ./ 1000 ; + params.center_wavelength = [1285 1287 1290 1292 1295]; + params.center_wavelength = 1310; + + params.channelspacing = [400e9]; + + params.random_zdw = [0]; + + %wh = warehouse :-) + wh = DataStorage(params); + + wh.showInfo; + + wh.addStorage("ber"); + + wh.addStorage("totalBer"); + +end + + + +%2) Simulate a bunch of data - TO BE IMPLEMENTED HERE - for now use scripts +%from Sebastian + +%3) Once the simulation folder is around, specifiy path and analyze dirs + +path = uigetdir('C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations'); + +allMat = getAllFilesInFolder(path,'.mat'); +allErr = getAllFilesInFolder(path,'.err'); + +%allMat = dir([path filesep '*.mat']); + +%allErr = dir([path filesep '*.err']); + +if numel(allMat) == 0 + warning('You defined an empty folder. Could not locate any .mat file.') +else + fprintf('%-20s', 'Err Files:'); fprintf('%-12s', num2str(numel(allErr))); fprintf('\n'); + fprintf('%-20s', 'Mat Files:'); fprintf('%-12s', num2str(numel(allMat))); fprintf('\n'); + fprintf('%-20s', 'Missing Mat Files:'); fprintf('%-12s', num2str(numel(allErr)-numel(allMat))); fprintf('\n'); +end + +%4) Now load that data + +f = waitbar(0,'Please wait...'); +cnt = 0; + +for num = 1:numel(allMat) + + fileName = allMat(num).name; + fileFolder = allMat(num).path; + fileExt = allMat(num).ext; +% + matFile = load([fileFolder filesep fileName fileExt]); + matFile = matFile.loop_data; + + % ____________________________________ + % FIND THE DATAPOINT CURRENTLY LOADED + zdw = 1310; + + channelplan = "symmetric"; + + channelspacing = str2double(strrep(regexp(fileName,'(_chsp)+([\d]*)','match'),'_chsp','')).*1e9; + + numchannels = str2double(strrep(regexp(fileName,'(ch)+(_)+([\d]*)','match'),'ch_','')); + + center_wavelength = str2double(insertAfter(strrep(regexp(fileName,'(lambda)+([\d]*)','match'),'lambda',''),4,'.')); + + center_wavelength = floor(center_wavelength * 1000) / 1000; + + if center_wavelength == 2192 + continue + end + + center_wavelength = 1310; + + random_zdw = str2double(strrep(regexp(fileName,'(rzwd)+([\d])','match'),'rzwd','')); + + l = str2double(strrep(regexp(fileName,'([L])+(_)+([\d]*)','match'),'L_','')); + + d = str2double(strrep(regexp(fileName,'([D])+(_)+([\d]*)','match'),'D_','')); + + if d == 0 + sgm = false; + else + sgm = true; + end + + + if numel(regexp(fileName,'(YYYY)','match')) > 1 + pol = "copolarized"; + elseif numel(regexp(fileName,'(YXXY)','match')) > 1 + pol = "paired"; + elseif numel(regexp(fileName,'(YXYX)','match')) > 1 + pol = "alternated"; + else + pol = "copolarized"; + end + + p_in = str2double(strrep(regexp(fileName,'(pow_)+([-,\d]{1})','match'),'pow_','')); + + pmd = 0.1; + + gamma = 0.0023; + + realiz = str2double(strrep(regexp(fileName,'(r)+([-,\d]{1,3})','match'),'r','')); + + + + % ____________________________________ + % Get the information you want from current file + rop=[]; + ber = []; + for pow = 2:12 + + module_number = ''; + for p = 1:11 %11 because there are 11 ROP branches in model + + % get ROP + if p == 1 + p_out = matFile.dp_optatten_para.atten; + else + p_out = matFile.("dp_optatten__"+(p)+"_para").atten; + end + + p_out = round(p_out-10*log10(numel(matFile.config.parameters.common.wavelengthPlan))); + + for c = 1:numel(matFile.config.parameters.common.wavelengthPlan) + + ber(c) = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,c}.ber; + + end + + totalBer = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,end}.totalBer; + + if totalBer > 0.2 && channelspacing == 400e9 && pol == "alternated" + disp("stopping here"); + pause; + end + + + % ____________________________________ + % Add value to warehouse at the correct position + + + + wh.addValueToStorage(ber,'ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw); + wh.getStoValue('ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw); + wh.addValueToStorage(totalBer,'totalBer',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels,center_wavelength,channelspacing,random_zdw); + + end + + end + waitbar(num/numel(allMat),f,'Loading your data'); +end + +close(f) + + + + + +% 4) Hey! the warehouse is here and (hopefully) filled with data :-) + +% Create a save dialog +defaultDir = 'C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\'; +defaultExt = '*.mat'; +[filename, pathname] = uiputfile(fullfile(defaultDir, defaultExt),'', 'wh.mat'); + +% Check if the user pressed Cancel +if isequal(filename, 0) || isequal(pathname, 0) + disp('Save operation canceled.'); +else + % Save the variable to the selected file + save(fullfile(pathname, filename), 'wh'); + disp(['Variable "wh" saved to: ', fullfile(pathname, filename)]); +end + + + + +function matFileStructArray = getAllFilesInFolder(folderPath,extension) + % Get a list of all files in the current folder + currentFolderFiles = dir(fullfile(folderPath, '*')); + + % Exclude '.' and '..' directories + currentFolderFiles = currentFolderFiles(~ismember({currentFolderFiles.name}, {'.', '..'})); + + % Initialize the structure array for .mat files + matFileStructArray = struct('path', {}, 'name', {}, 'ext', {}); + + % Loop over each file in the current folder + for i = 1:length(currentFolderFiles) + currentFile = currentFolderFiles(i); + + % Check if the current item is a file and has a .mat extension + if ~currentFile.isdir && endsWith(currentFile.name, extension, 'IgnoreCase', true) + % If it's a .mat file, add it to the structure array + [matFileStructArray(end + 1).path,matFileStructArray(end+1).name, matFileStructArray(end+1).ext] = fileparts(fullfile(folderPath, currentFile.name)); + elseif currentFile.isdir + % If it's a directory, recursively call the function + subfolderPath = fullfile(folderPath, currentFile.name); + subfolderMatFiles = getAllFilesInFolder(subfolderPath,extension); + + % Add .mat files from the subfolder to the structure array + matFileStructArray = [matFileStructArray, subfolderMatFiles]; + end + end +end + + diff --git a/Classes/Warehouse_class/functions/fwm_plots/CompleteRoutine_DifferentChannels_Fig3.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/CompleteRoutine_DifferentChannels_Fig3.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/CompleteRoutine_DifferentChannels_Fig3.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/CompleteRoutine_DifferentChannels_Fig3.m index aaec117..170fee9 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/CompleteRoutine_DifferentChannels_Fig3.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/CompleteRoutine_DifferentChannels_Fig3.m @@ -1,250 +1,250 @@ -% Script, that shows the data management routine :-) - -loadExistingWareHouse = 0; - -if loadExistingWareHouse - - [file, path] = uigetfile(); - wh = load([path filesep file]); - wh = wh.wh; - wh.showInfo; - -else - - % 1) Define all your parameters, best practice directly constructs a - % structure - - params = struct; - - params.l = [2, 10]; - - params.dispersion = [0, 3]; - - params.sgm = [0, 1]; - -% params.pol = ["YXYXYXYX","YXXYYXXY","YYYYYYYY"]; - params.pol = ["alternated","paired","copolarized"]; - - params.p_in = [3]; - - params.p_out = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2]; - - params.pmd = [0.1]; - - params.gamma = [0.0023]; - - params.realization = [1:20]; - - params.numchannels = [1,2,4,8,16]; - - params.center_wavelength = floor([getSweepWavelengths(35, 50e9, 1310)] .* 1000) ./ 1000 ; - params.center_wavelength = [1285 1287 1290 1292 1295]; - params.center_wavelength = 1310; - - params.channelspacing = [400e9]; - - params.random_zdw = [0,1]; - - %wh = warehouse :-) - wh = DataStorage(params); - - wh.showInfo; - - wh.addStorage("ber"); - - wh.addStorage("totalBer"); - -end - - - -%2) Simulate a bunch of data - TO BE IMPLEMENTED HERE - for now use scripts -%from Sebastian - -%3) Once the simulation folder is around, specifiy path and analyze dirs - -path = uigetdir('C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations'); - -allMat = getAllFilesInFolder(path,'.mat'); -allErr = getAllFilesInFolder(path,'.err'); - -%allMat = dir([path filesep '*.mat']); - -%allErr = dir([path filesep '*.err']); - -if numel(allMat) == 0 - warning('You defined an empty folder. Could not locate any .mat file.') -else - fprintf('%-20s', 'Err Files:'); fprintf('%-12s', num2str(numel(allErr))); fprintf('\n'); - fprintf('%-20s', 'Mat Files:'); fprintf('%-12s', num2str(numel(allMat))); fprintf('\n'); - fprintf('%-20s', 'Missing Mat Files:'); fprintf('%-12s', num2str(numel(allErr)-numel(allMat))); fprintf('\n'); -end - -%4) Now load that data - -f = waitbar(0,'Please wait...'); -cnt = 0; - -for num = 1:numel(allMat) - - fileName = allMat(num).name; - fileFolder = allMat(num).path; - fileExt = allMat(num).ext; -% - matFile = load([fileFolder filesep fileName fileExt]); - matFile = matFile.loop_data; - - % ____________________________________ - % FIND THE DATAPOINT CURRENTLY LOADED - zdw = 1310; - - channelplan = "symmetric"; - - channelspacing = str2double(strrep(regexp(fileName,'(_chsp)+([\d]*)','match'),'_chsp','')).*1e9; - - numchannels = str2double(strrep(regexp(fileName,'(ch)+(_)+([\d]*)','match'),'ch_','')); - - center_wavelength = str2double(insertAfter(strrep(regexp(fileName,'(lambda)+([\d]*)','match'),'lambda',''),4,'.')); - - center_wavelength = floor(center_wavelength * 1000) / 1000; - - if center_wavelength == 2192 - continue - end - - center_wavelength = 1310; - - random_zdw = str2double(strrep(regexp(fileName,'(rzwd)+([\d])','match'),'rzwd','')); - - l = str2double(strrep(regexp(fileName,'([L])+(_)+([\d]*)','match'),'L_','')); - - d = str2double(strrep(regexp(fileName,'([D])+(_)+([\d]*)','match'),'D_','')); - - if d == 0 - sgm = false; - else - sgm = true; - end - - - if numel(regexp(fileName,'(YYYY)','match')) > 1 - pol = "copolarized"; - elseif numel(regexp(fileName,'(YXXY)','match')) > 1 - pol = "paired"; - elseif numel(regexp(fileName,'(YXYX)','match')) > 1 - pol = "alternated"; - else - pol = "copolarized"; - end - - p_in = str2double(strrep(regexp(fileName,'(pow_)+([-,\d]{1})','match'),'pow_','')); - - pmd = 0.1; - - gamma = 0.0023; - - realiz = str2double(strrep(regexp(fileName,'(r)+([-,\d]{1,3})','match'),'r','')); - - - - % ____________________________________ - % Get the information you want from current file - rop=[]; - ber = []; - for pow = 2:12 - - module_number = ''; - for p = 1:11 %11 because there are 11 ROP branches in model - - % get ROP - if p == 1 - p_out = matFile.dp_optatten_para.atten; - else - p_out = matFile.("dp_optatten__"+(p)+"_para").atten; - end - - p_out = round(p_out-10*log10(numel(matFile.config.parameters.common.wavelengthPlan))); - - for c = 1:numel(matFile.config.parameters.common.wavelengthPlan) - - ber(c) = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,c}.ber; - - end - - totalBer = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,end}.totalBer; - - if totalBer > 0.2 && channelspacing == 400e9 && pol == "alternated" - disp("stopping here"); - pause; - end - - % ____________________________________ - % Add value to warehouse at the correct position - - - - wh.addValueToStorage(ber,'ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw); - wh.getStoValue('ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw); - wh.addValueToStorage(totalBer,'totalBer',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels,center_wavelength,channelspacing,random_zdw); - - end - - end - waitbar(num/numel(allMat),f,'Loading your data'); -end - -close(f) - - - - - -% 4) Hey! the warehouse is here and (hopefully) filled with data :-) - -% Create a save dialog -defaultDir = 'C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\'; -defaultExt = '*.mat'; -[filename, pathname] = uiputfile(fullfile(defaultDir, defaultExt),'', 'wh.mat'); - -% Check if the user pressed Cancel -if isequal(filename, 0) || isequal(pathname, 0) - disp('Save operation canceled.'); -else - % Save the variable to the selected file - save(fullfile(pathname, filename), 'wh'); - disp(['Variable "wh" saved to: ', fullfile(pathname, filename)]); -end - - - - -function matFileStructArray = getAllFilesInFolder(folderPath,extension) - % Get a list of all files in the current folder - currentFolderFiles = dir(fullfile(folderPath, '*')); - - % Exclude '.' and '..' directories - currentFolderFiles = currentFolderFiles(~ismember({currentFolderFiles.name}, {'.', '..'})); - - % Initialize the structure array for .mat files - matFileStructArray = struct('path', {}, 'name', {}, 'ext', {}); - - % Loop over each file in the current folder - for i = 1:length(currentFolderFiles) - currentFile = currentFolderFiles(i); - - % Check if the current item is a file and has a .mat extension - if ~currentFile.isdir && endsWith(currentFile.name, extension, 'IgnoreCase', true) - % If it's a .mat file, add it to the structure array - [matFileStructArray(end + 1).path,matFileStructArray(end+1).name, matFileStructArray(end+1).ext] = fileparts(fullfile(folderPath, currentFile.name)); - elseif currentFile.isdir - % If it's a directory, recursively call the function - subfolderPath = fullfile(folderPath, currentFile.name); - subfolderMatFiles = getAllFilesInFolder(subfolderPath,extension); - - % Add .mat files from the subfolder to the structure array - matFileStructArray = [matFileStructArray, subfolderMatFiles]; - end - end -end - - +% Script, that shows the data management routine :-) + +loadExistingWareHouse = 0; + +if loadExistingWareHouse + + [file, path] = uigetfile(); + wh = load([path filesep file]); + wh = wh.wh; + wh.showInfo; + +else + + % 1) Define all your parameters, best practice directly constructs a + % structure + + params = struct; + + params.l = [2, 10]; + + params.dispersion = [0, 3]; + + params.sgm = [0, 1]; + +% params.pol = ["YXYXYXYX","YXXYYXXY","YYYYYYYY"]; + params.pol = ["alternated","paired","copolarized"]; + + params.p_in = [3]; + + params.p_out = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2]; + + params.pmd = [0.1]; + + params.gamma = [0.0023]; + + params.realization = [1:20]; + + params.numchannels = [1,2,4,8,16]; + + params.center_wavelength = floor([getSweepWavelengths(35, 50e9, 1310)] .* 1000) ./ 1000 ; + params.center_wavelength = [1285 1287 1290 1292 1295]; + params.center_wavelength = 1310; + + params.channelspacing = [400e9]; + + params.random_zdw = [0,1]; + + %wh = warehouse :-) + wh = DataStorage(params); + + wh.showInfo; + + wh.addStorage("ber"); + + wh.addStorage("totalBer"); + +end + + + +%2) Simulate a bunch of data - TO BE IMPLEMENTED HERE - for now use scripts +%from Sebastian + +%3) Once the simulation folder is around, specifiy path and analyze dirs + +path = uigetdir('C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations'); + +allMat = getAllFilesInFolder(path,'.mat'); +allErr = getAllFilesInFolder(path,'.err'); + +%allMat = dir([path filesep '*.mat']); + +%allErr = dir([path filesep '*.err']); + +if numel(allMat) == 0 + warning('You defined an empty folder. Could not locate any .mat file.') +else + fprintf('%-20s', 'Err Files:'); fprintf('%-12s', num2str(numel(allErr))); fprintf('\n'); + fprintf('%-20s', 'Mat Files:'); fprintf('%-12s', num2str(numel(allMat))); fprintf('\n'); + fprintf('%-20s', 'Missing Mat Files:'); fprintf('%-12s', num2str(numel(allErr)-numel(allMat))); fprintf('\n'); +end + +%4) Now load that data + +f = waitbar(0,'Please wait...'); +cnt = 0; + +for num = 1:numel(allMat) + + fileName = allMat(num).name; + fileFolder = allMat(num).path; + fileExt = allMat(num).ext; +% + matFile = load([fileFolder filesep fileName fileExt]); + matFile = matFile.loop_data; + + % ____________________________________ + % FIND THE DATAPOINT CURRENTLY LOADED + zdw = 1310; + + channelplan = "symmetric"; + + channelspacing = str2double(strrep(regexp(fileName,'(_chsp)+([\d]*)','match'),'_chsp','')).*1e9; + + numchannels = str2double(strrep(regexp(fileName,'(ch)+(_)+([\d]*)','match'),'ch_','')); + + center_wavelength = str2double(insertAfter(strrep(regexp(fileName,'(lambda)+([\d]*)','match'),'lambda',''),4,'.')); + + center_wavelength = floor(center_wavelength * 1000) / 1000; + + if center_wavelength == 2192 + continue + end + + center_wavelength = 1310; + + random_zdw = str2double(strrep(regexp(fileName,'(rzwd)+([\d])','match'),'rzwd','')); + + l = str2double(strrep(regexp(fileName,'([L])+(_)+([\d]*)','match'),'L_','')); + + d = str2double(strrep(regexp(fileName,'([D])+(_)+([\d]*)','match'),'D_','')); + + if d == 0 + sgm = false; + else + sgm = true; + end + + + if numel(regexp(fileName,'(YYYY)','match')) > 1 + pol = "copolarized"; + elseif numel(regexp(fileName,'(YXXY)','match')) > 1 + pol = "paired"; + elseif numel(regexp(fileName,'(YXYX)','match')) > 1 + pol = "alternated"; + else + pol = "copolarized"; + end + + p_in = str2double(strrep(regexp(fileName,'(pow_)+([-,\d]{1})','match'),'pow_','')); + + pmd = 0.1; + + gamma = 0.0023; + + realiz = str2double(strrep(regexp(fileName,'(r)+([-,\d]{1,3})','match'),'r','')); + + + + % ____________________________________ + % Get the information you want from current file + rop=[]; + ber = []; + for pow = 2:12 + + module_number = ''; + for p = 1:11 %11 because there are 11 ROP branches in model + + % get ROP + if p == 1 + p_out = matFile.dp_optatten_para.atten; + else + p_out = matFile.("dp_optatten__"+(p)+"_para").atten; + end + + p_out = round(p_out-10*log10(numel(matFile.config.parameters.common.wavelengthPlan))); + + for c = 1:numel(matFile.config.parameters.common.wavelengthPlan) + + ber(c) = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,c}.ber; + + end + + totalBer = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,end}.totalBer; + + if totalBer > 0.2 && channelspacing == 400e9 && pol == "alternated" + disp("stopping here"); + pause; + end + + % ____________________________________ + % Add value to warehouse at the correct position + + + + wh.addValueToStorage(ber,'ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw); + wh.getStoValue('ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw); + wh.addValueToStorage(totalBer,'totalBer',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels,center_wavelength,channelspacing,random_zdw); + + end + + end + waitbar(num/numel(allMat),f,'Loading your data'); +end + +close(f) + + + + + +% 4) Hey! the warehouse is here and (hopefully) filled with data :-) + +% Create a save dialog +defaultDir = 'C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\'; +defaultExt = '*.mat'; +[filename, pathname] = uiputfile(fullfile(defaultDir, defaultExt),'', 'wh.mat'); + +% Check if the user pressed Cancel +if isequal(filename, 0) || isequal(pathname, 0) + disp('Save operation canceled.'); +else + % Save the variable to the selected file + save(fullfile(pathname, filename), 'wh'); + disp(['Variable "wh" saved to: ', fullfile(pathname, filename)]); +end + + + + +function matFileStructArray = getAllFilesInFolder(folderPath,extension) + % Get a list of all files in the current folder + currentFolderFiles = dir(fullfile(folderPath, '*')); + + % Exclude '.' and '..' directories + currentFolderFiles = currentFolderFiles(~ismember({currentFolderFiles.name}, {'.', '..'})); + + % Initialize the structure array for .mat files + matFileStructArray = struct('path', {}, 'name', {}, 'ext', {}); + + % Loop over each file in the current folder + for i = 1:length(currentFolderFiles) + currentFile = currentFolderFiles(i); + + % Check if the current item is a file and has a .mat extension + if ~currentFile.isdir && endsWith(currentFile.name, extension, 'IgnoreCase', true) + % If it's a .mat file, add it to the structure array + [matFileStructArray(end + 1).path,matFileStructArray(end+1).name, matFileStructArray(end+1).ext] = fileparts(fullfile(folderPath, currentFile.name)); + elseif currentFile.isdir + % If it's a directory, recursively call the function + subfolderPath = fullfile(folderPath, currentFile.name); + subfolderMatFiles = getAllFilesInFolder(subfolderPath,extension); + + % Add .mat files from the subfolder to the structure array + matFileStructArray = [matFileStructArray, subfolderMatFiles]; + end + end +end + + diff --git a/Classes/Warehouse_class/functions/fwm_plots/automate_JLT_plots.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_JLT_plots.m similarity index 89% rename from Classes/Warehouse_class/functions/fwm_plots/automate_JLT_plots.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_JLT_plots.m index 68b77b6..c29a009 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/automate_JLT_plots.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_JLT_plots.m @@ -1,394 +1,415 @@ - - -%automate plots -[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_februar_24\wh_mi_nacht.mat"); -wh = load([path filesep file]); -wh = wh.wh; - -plotJob = struct(); -width = 350; -height = 200; -plotJob.Position = [100 100 width 100+height]; -cols = cbrewer2("paired",12); -plotJob.color = cols(1,:); -plotJob.l = 2; -plotJob.ch = 16; -plotJob.d = 0; -plotJob.sgm = 0; -plotJob.pol = "copolarized"; -plotJob.p_in = 3; -plotJob.gamma = 0; -plotJob.pmd = 0; -plotJob.channelspacing = 400e9; -plotJob.randzdw = 0; - - -plotJob.plot_ber_curve = 1; -plotJob.plot_3dber_curve = 0; -plotJob.plot_violin = 0; -plotJob.plot_wavelength_sweep = 0; -plotJob.plot_wavelength_sweep_failure_rate = 0; - - -plotJob.dataStatArg = 'Lineplot with quartiles'; -plotJob.plotTypeArg = 'Lines'; -plotJob.displayname = 'a'; -plotJob.title = 'title'; -plotJob.figName = '16 Chann__'; -plotJob.xAxisLabel = 'ROP per Channel in dBm'; -plotJob.yAxisLabel = 'BER'; - -% createbercurves(wh,plotJob) -createviolinplots(wh,plotJob); -% createsweepplots(wh,plotJob); - - -%% 1 -function createbercurves(wh,plotJob) -width = 1650; -height = 400; -s = 100; -e = 100; - -cols = cbrewer2("paired",12); -numRows = 2; -numCols = 4; - -plotJob.figName = '16 Chann_200G'; -plotJob.channelspacing = 400e9; -plotJob.ch = 16; -Len = [2,2,2,2,10,10,10,10]; -Pol = ["copolarized","alternated","paired","copolarized","copolarized","alternated","paired","copolarized"]; -Title = ["Co Polarized","Alternating Pol. Interl.","Paired Pol. Interl.","Link Segmentation",]; -D = [0,0,0,3,0,0,0,3]; -Sgm = [0,0,0,1,0,0,0,1]; - -colidx = [4,8,6]; -P_launch = [0,3,6]; - -fig = figure('Name',plotJob.figName); -fig.Position = plotJob.Position; -fig.Units = "centimeters"; -fig.Position = [0 0 18 7]; -t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact'); -for idx = 1:(numRows * numCols) - % Create subplot - % sp = subplot(numRows, numCols, idx); - nexttile; - plotJob.l = Len(idx); - plotJob.pol = Pol(idx); - plotJob.d = D(idx); - plotJob.sgm = Sgm(idx); - plotJob.randzdw = 1; - - for i = 1:3 - - plotJob.p_in = P_launch(i); - plotJob.color = cols(colidx(i),:); - hold on - plotCurve(wh, plotJob); - - end - % - if idx ~= 1 && idx ~= 5 % For example, hide y-axis for subplot 1 - set(gca, 'YTickLabel',[]); % Hide y-axis ticks and labels - set(gca,'YGrid','on'); - set(gca, 'YLabel', []); - end - if idx ~= 5 && idx ~= 6 && idx ~= 7 && idx ~= 8 - set(gca, 'XLabel', []); - set(gca, 'XTickLabel', []); - - end - grid on - - g = gca; - pos = g.Position; - if idx <= 4 - title(Title(idx),'FontSize',8); - % a = annotation('textbox', pos-[0.0020 -0.1434 0.0947 0.3121], 'String', "FEC: 3.8e-3","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','middle','FitBoxToText','on'); - % a = annotation('textbox', pos, 'String', "2 km","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','bottom','FitBoxToText','on'); - else - % a = annotation('textbox', pos, 'String', "FEC: 3.8e-3","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','middle','FitBoxToText','on'); - % a = annotation('textbox', pos, 'String', "10 km","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','bottom','FitBoxToText','on'); - end - -end - -% Create textbox -annotation(fig,'textbox',... - [0.0696078431372547 0.246851385390432 0.0656862745098043 0.0453400503778337],... - 'String','10 km',... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.288235294117647 0.239294710327459 0.0656862745098043 0.0453400503778338],... - 'String','10 km',... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.516666666666666 0.241813602015117 0.0656862745098042 0.0453400503778338],... - 'String','10 km',... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.742156862745097 0.236775818639802 0.0656862745098042 0.0453400503778339],... - 'String','10 km',... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.071996471804854 0.578899159967702 0.0656862745098039 0.0453400503778341],... - 'String',{'2 km'},... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.29596893566113 0.576253721089996 0.0656862745098041 0.0453400503778341],... - 'String',{'2 km'},... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.524883914268912 0.580785315705112 0.0656862745098041 0.0453400503778341],... - 'String',{'2 km'},... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.753758338909501 0.581291504465311 0.0656862745098037 0.0453400503778341],... - 'String',{'2 km'},... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -a=sgtitle(['N=',num2str(plotJob.ch),'; $\Delta f_{\mathrm{ch}}$= ',num2str(plotJob.channelspacing*1e-9),' GHz'],'FontSIze',10); -a.Interpreter = "latex"; - -lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','Interpreter','latex'); -lgd.NumColumns = 3; -lgd.Layout.Tile = 'south'; - -copygraphics(t,'BackgroundColor','none'); -end - -%% 2 -function createviolinplots(wh,plotJob) -width = 350; -height = 200; -s = 100; -e = 100; - -cols = cbrewer2("paired",12); -numRows = 1; -numCols = 4; - - -plotJob.ch = 16; -plotJob.p_in = 3; -plotJob.randzdw = 0; - -Pol = ["copolarized","copolarized","alternated","paired",]; -Title = ["Co Pol.","Link Segmentation","Paired Pol. Interl.","Alternating Pol. Interl."]; -D = [0,3,0,0]; -Sgm = [0,1,0,0]; - -colidx = [2]; -Len = [2]; - -plotJob.figName = ['_Violin',num2str(plotJob.ch),' Channels; ',num2str(plotJob.channelspacing*1e-9),' GHz; ',num2str(plotJob.p_in),' dBm; randomized: ', num2str(plotJob.randzdw)]; -fig = figure('Name',plotJob.figName); -fig.Position = plotJob.Position; -fig.Units = "centimeters"; -fig.Position = [0 0 18 7]; - -t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact'); -for idx = 1:(numRows * numCols) - % Create subplot - %subplot(numRows, numCols, idx); - nexttile; - - plotJob.pol = Pol(idx); - plotJob.d = D(idx); - plotJob.sgm = Sgm(idx); - - for i = 1 - - plotJob.color = cols(colidx(i),:); - plotJob.l = Len(i); - hold on - plotViolin(wh, plotJob); - - end - - if idx ~= 1 % For example, hide y-axis for subplot 1 - %set(gca, 'YTickLabel',[]); % Hide y-axis ticks and labels - set(gca, 'YGrid','on'); - set(gca, 'YLabel', []); - end - - if idx <= 4 - title(Title(idx)); - end - - -end - -% Create textbox -annotation(fig,'textbox',... - [0.300019607843137 0.816120906801009 0.108803921568628 0.0906801007556676],... - 'String','$P_{\mathrm{in}}=3$ dBm',... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.530411764705882 0.81612090680101 0.108803921568628 0.0906801007556676],... - 'String','$P_{\mathrm{in}}=3$ dBm',... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.755901960784313 0.816120906801011 0.108803921568628 0.0906801007556676],... - 'String','$P_{\mathrm{in}}=3$ dBm',... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -% Create textbox -annotation(fig,'textbox',... - [0.0696274509803918 0.584382871536529 0.108803921568628 0.0906801007556676],... - 'String','$P_{\mathrm{in}}=3$ dBm',... - 'LineStyle','none',... - 'Interpreter','latex',... - 'FontSize',8,... - 'FitBoxToText','off'); - -copygraphics(t,'BackgroundColor','none'); -% lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','Interpreter','latex'); -% lgd.NumColumns = 3; -% lgd.Layout.Tile = 'south'; - -end - -%% 3 -function createsweepplots(wh,plotJob) - -width = 650; -height = 200; -s = 100; -e = 100; -plotJob.Position = [0 0 width e+height]; - -cols = cbrewer2("paired",12); -numRows = 1; -numCols = 4; - - -plotJob.channelspacing = 200e9; -plotJob.ch = 16; -plotJob.randzdw = 1; -plotJob.l = 10; - -plotJob.p_in = 3; - -Pol = ["copolarized","alternated","paired","copolarized"]; -Title = ["Co Polarized","Alternating Pol. Interl.","Paired Pol. Interl.","Link Segmentation",]; -D = [0,0,0,3]; -Sgm = [0,0,0,1]; -Channelspacing = [200e9, 200e9]; -PlotTypeArg = ["--","-"]; -colidx = [6,8,2,4]; -Len = [2,10]; - -plotJob.figName = [num2str(plotJob.ch),num2str(plotJob.channelspacing*1e-9),num2str(plotJob.p_in),'...']; -plotJob.figName = "10km 400ghz"; - - - - -fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); - -if isvalid(fig) - figure(fig) - fig = get(fig); - AxesMain = fig.CurrentAxes; - hold on -else - fig = figure('name',char(plotJob.figName)); - AxesMain = gca; - hold on -end - -fig.Position = plotJob.Position; -fig.Units = "centimeters"; -fig.Position = [0 0 18 7]; - -for j = 1 - - plotJob.channelspacing = Channelspacing(j); - plotJob.plotTypeArg = PlotTypeArg(j); - - for idx = 1:4 - - plotJob.color = cols(colidx(idx),:); - plotJob.pol = Pol(idx); - plotJob.d = D(idx); - plotJob.sgm = Sgm(idx); - plotJob.displayname = [char(plotJob.pol)]; - hold on - plotBerVsZdwFailureRate(wh, plotJob); - - end -end -legend('Location', 'southoutside', 'Orientation', 'horizontal'); - - -%plot channel positions -hold on -chpos = calcWavelengthPlan(plotJob.ch, plotJob.channelspacing, 1310); -xline(chpos,'LineWidth',2,'Alpha',0.4,'HandleVisibility','off'); - -chpos = calcWavelengthPlan(plotJob.ch, plotJob.channelspacing, chpos(4)); -xline(chpos,'LineWidth',2,'LineStyle','--','Alpha',0.1,'HandleVisibility','off'); - -title(['N=',num2str(plotJob.ch),' $\Delta f_{\mathrm{ch}}$= ',num2str(plotJob.channelspacing*1e-9),' GHz'],'FontSize',10,'Interpreter','latex'); - - - - -end - - - + + +%automate plots +[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_februar_24\wh_mi_nacht.mat"); +wh = load([path filesep file]); +wh = wh.wh; + +% fields = fieldnames(wh.parameter); +% for k = 1:numel(fields) +% oldParam = wh.parameter.(fields{k}); +% % copy over the properties to your new class +% wh.parameter.(fields{k}) = StorageParameter(... +% oldParam.Name, oldParam.values); +% end +% + + +plotJob = struct(); +width = 350; +height = 200; +plotJob.Position = [100 100 width 100+height]; +cols = cbrewer2("paired",12); +plotJob.color = cols(1,:); +plotJob.l = 10; +plotJob.ch = 16; +plotJob.d = 0; +plotJob.sgm = 0; +plotJob.pol = "copolarized"; +plotJob.p_in = 3; +plotJob.gamma = 0.0023; +plotJob.pmd = 0.1; +plotJob.channelspacing = 400e9; +plotJob.randzdw = 1; + + +plotJob.plot_ber_curve = 0; +plotJob.plot_3dber_curve = 0; +plotJob.plot_violin = 1; +plotJob.plot_wavelength_sweep = 0; +plotJob.plot_wavelength_sweep_failure_rate = 0; + + +plotJob.dataStatArg = 'Lineplot with quartiles'; +plotJob.plotTypeArg = 'Lines'; +plotJob.displayname = 'a'; +plotJob.title = 'title'; +plotJob.figName = '16 Chann'; +plotJob.xAxisLabel = 'ROP per Channel in dBm'; +plotJob.yAxisLabel = 'BER'; + +%% + + % createbercurves(wh,plotJob) + + P = [3]; + for i = 1:2 + plotJob.p_in = P(i); + createviolinplots(wh,plotJob); + end + % createsweepplots(wh,plotJob); + + +%% 1 +function createbercurves(wh,plotJob) +width = 1650; +height = 400; +s = 100; +e = 100; + +cols = cbrewer2("paired",12); +numRows = 2; +numCols = 4; + +plotJob.figName = '16 Chann_200G'; +plotJob.channelspacing = 400e9; +plotJob.ch = 16; +Len = [2,2,2,2,10,10,10,10]; +Pol = ["copolarized","alternated","paired","copolarized","copolarized","alternated","paired","copolarized"]; +Title = ["Co Polarized","Alternating Pol. Interl.","Paired Pol. Interl.","Link Segmentation",]; +D = [0,0,0,3,0,0,0,3]; +Sgm = [0,0,0,1,0,0,0,1]; + +colidx = [4,8,6]; +P_launch = [3,6]; + +fig = figure('Name',plotJob.figName); +fig.Position = plotJob.Position; +fig.Units = "centimeters"; +fig.Position = [0 0 18 7]; +t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact'); +for idx = 1:(numRows * numCols) + % Create subplot + % sp = subplot(numRows, numCols, idx); + nexttile; + plotJob.l = Len(idx); + plotJob.pol = Pol(idx); + plotJob.d = D(idx); + plotJob.sgm = Sgm(idx); + plotJob.randzdw = 1; + + for i = 1:length(P_launch) + + plotJob.p_in = P_launch(i); + plotJob.color = cols(colidx(i),:); + hold on + plotCurve(wh, plotJob); + + end + % + if idx ~= 1 && idx ~= 5 % For example, hide y-axis for subplot 1 + set(gca, 'YTickLabel',[]); % Hide y-axis ticks and labels + set(gca,'YGrid','on'); + set(gca, 'YLabel', []); + end + if idx ~= 5 && idx ~= 6 && idx ~= 7 && idx ~= 8 + set(gca, 'XLabel', []); + set(gca, 'XTickLabel', []); + + end + grid on + + g = gca; + pos = g.Position; + if idx <= 4 + title(Title(idx),'FontSize',8); + % a = annotation('textbox', pos-[0.0020 -0.1434 0.0947 0.3121], 'String', "FEC: 3.8e-3","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','middle','FitBoxToText','on'); + % a = annotation('textbox', pos, 'String', "2 km","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','bottom','FitBoxToText','on'); + else + % a = annotation('textbox', pos, 'String', "FEC: 3.8e-3","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','middle','FitBoxToText','on'); + % a = annotation('textbox', pos, 'String', "10 km","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','bottom','FitBoxToText','on'); + end + +end + +% Create textbox +annotation(fig,'textbox',... + [0.0696078431372547 0.246851385390432 0.0656862745098043 0.0453400503778337],... + 'String','10 km',... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.288235294117647 0.239294710327459 0.0656862745098043 0.0453400503778338],... + 'String','10 km',... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.516666666666666 0.241813602015117 0.0656862745098042 0.0453400503778338],... + 'String','10 km',... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.742156862745097 0.236775818639802 0.0656862745098042 0.0453400503778339],... + 'String','10 km',... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.071996471804854 0.578899159967702 0.0656862745098039 0.0453400503778341],... + 'String',{'2 km'},... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.29596893566113 0.576253721089996 0.0656862745098041 0.0453400503778341],... + 'String',{'2 km'},... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.524883914268912 0.580785315705112 0.0656862745098041 0.0453400503778341],... + 'String',{'2 km'},... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.753758338909501 0.581291504465311 0.0656862745098037 0.0453400503778341],... + 'String',{'2 km'},... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +a=sgtitle(['N=',num2str(plotJob.ch),'; $\Delta f_{\mathrm{ch}}$= ',num2str(plotJob.channelspacing*1e-9),' GHz'],'FontSIze',10); +a.Interpreter = "latex"; + +lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','Interpreter','latex'); +lgd.NumColumns = 3; +lgd.Layout.Tile = 'south'; + +copygraphics(t,'BackgroundColor','none'); +end + +%% 2 +function createviolinplots(wh,plotJob) + +width = 350; +height = 200; +s = 100; +e = 100; + +cols = cbrewer2("paired",12); +numRows = 1; +numCols = 4; + +plotJob.ch = 16; +plotJob.randzdw = 1; + +Pol = ["copolarized","copolarized","alternated","paired",]; +Title = ["Co Pol.","Link Segmentation","Paired Pol. Interl.","Alternating Pol. Interl."]; +D = [0,3,0,0]; +Sgm = [0,1,0,0]; + +colidx = [4]; +Len = plotJob.l; + +fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); +if isvalid(fig) + figure(fig) + % fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on + % t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact'); +else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on; grid on; + % t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact'); +end + +for idx = 1:(numRows * numCols) + % Create subplot + subplot(numRows, numCols, idx); + + plotJob.pol = Pol(idx); + plotJob.d = D(idx); + plotJob.sgm = Sgm(idx); + + for i = 1 + + plotJob.color = cols(colidx(i),:); + plotJob.l = Len(i); + hold on + plotViolin(wh, plotJob); + + end + + if idx ~= 1 % For example, hide y-axis for subplot 1 + %set(gca, 'YTickLabel',[]); % Hide y-axis ticks and labels + set(gca, 'YGrid','on'); + set(gca, 'YLabel', []); + end + + if idx <= 4 + title(Title(idx)); + end + + +end + +% Create textbox +annotation(fig,'textbox',... + [0.300019607843137 0.816120906801009 0.108803921568628 0.0906801007556676],... + 'String','$P_{\mathrm{in}}=3$ dBm',... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.530411764705882 0.81612090680101 0.108803921568628 0.0906801007556676],... + 'String','$P_{\mathrm{in}}=3$ dBm',... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.755901960784313 0.816120906801011 0.108803921568628 0.0906801007556676],... + 'String','$P_{\mathrm{in}}=3$ dBm',... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% Create textbox +annotation(fig,'textbox',... + [0.0696274509803918 0.584382871536529 0.108803921568628 0.0906801007556676],... + 'String','$P_{\mathrm{in}}=3$ dBm',... + 'LineStyle','none',... + 'Interpreter','latex',... + 'FontSize',8,... + 'FitBoxToText','off'); + +% lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','Interpreter','latex'); +% lgd.NumColumns = 3; +% lgd.Layout.Tile = 'south'; + +end + +%% 3 +function createsweepplots(wh,plotJob) + +width = 650; +height = 200; +s = 100; +e = 100; +plotJob.Position = [0 0 width e+height]; + +cols = cbrewer2("paired",12); +numRows = 1; +numCols = 4; + + +plotJob.channelspacing = 200e9; +plotJob.ch = 16; +plotJob.randzdw = 1; +plotJob.l = 10; + +plotJob.p_in = 3; + +Pol = ["copolarized","alternated","paired","copolarized"]; +Title = ["Co Polarized","Alternating Pol. Interl.","Paired Pol. Interl.","Link Segmentation",]; +D = [0,0,0,3]; +Sgm = [0,0,0,1]; +Channelspacing = [200e9, 200e9]; +PlotTypeArg = ["--","-"]; +colidx = [6,8,2,4]; +Len = [2,10]; + +plotJob.figName = [num2str(plotJob.ch),num2str(plotJob.channelspacing*1e-9),num2str(plotJob.p_in),'...']; +plotJob.figName = "10km 400ghz"; + + + + +fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); + +if isvalid(fig) + figure(fig) + fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on +else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on +end + +fig.Position = plotJob.Position; +fig.Units = "centimeters"; +fig.Position = [0 0 18 7]; + +for j = 1 + + plotJob.channelspacing = Channelspacing(j); + plotJob.plotTypeArg = PlotTypeArg(j); + + for idx = 1:4 + + plotJob.color = cols(colidx(idx),:); + plotJob.pol = Pol(idx); + plotJob.d = D(idx); + plotJob.sgm = Sgm(idx); + plotJob.displayname = [char(plotJob.pol)]; + hold on + plotBerVsZdwFailureRate(wh, plotJob); + + end +end +legend('Location', 'southoutside', 'Orientation', 'horizontal'); + + +%plot channel positions +hold on +chpos = calcWavelengthPlan(plotJob.ch, plotJob.channelspacing, 1310); +xline(chpos,'LineWidth',2,'Alpha',0.4,'HandleVisibility','off'); + +chpos = calcWavelengthPlan(plotJob.ch, plotJob.channelspacing, chpos(4)); +xline(chpos,'LineWidth',2,'LineStyle','--','Alpha',0.1,'HandleVisibility','off'); + +title(['N=',num2str(plotJob.ch),' $\Delta f_{\mathrm{ch}}$= ',num2str(plotJob.channelspacing*1e-9),' GHz'],'FontSize',10,'Interpreter','latex'); + + + + +end + + + diff --git a/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_PTL_plot_new.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_PTL_plot_new.m new file mode 100644 index 0000000..f92f86f --- /dev/null +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_PTL_plot_new.m @@ -0,0 +1,137 @@ +% Select dataset +[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_februar_24\wh_mi_nacht.mat"); +wh = load(fullfile(path, file)); +wh = wh.wh; + +%% --- Plot Settings --- +cols = cbrewer2("Paired", 12); + +plotJob = struct(); +plotJob.Position = [100 100 600 400]; +plotJob.channelspacing = 400e9; +plotJob.ch = 16; +plotJob.d = 0; +plotJob.sgm = 0; +plotJob.gamma = 0.0023; +plotJob.pmd = 0.1; +plotJob.randzdw = 1; +plotJob.plot_ber_curve = 1; +plotJob.xAxisLabel = 'ROP per $\lambda$ [dBm]'; +plotJob.yAxisLabel = 'BER'; +plotJob.figName = 'avg BER_vs_Plaunch_combined'; +plotJob.dataStatArg = 'Lineplot with quartiles';%'All Channels; mean(PMD Realizations)';Lineplot with quartiles +plotJob.plotTypeArg = 'Lines'; +plotJob.displayname = 'bla'; +% --- Parameter combinations --- +Len = [2, 10]; +Pol = ["copolarized", "copolarized", "alternated", "paired"]; +Title = ["CoPol","LS", "API", "PPI"]; +D = [0, 3, 0, 0]; +Sgm = [0, 1, 0, 0]; +% Pol = ["copolarized", "copolarized"]; +% Title = ["CoPol","LS"]; +% D = [0, 3]; +% Sgm = [0, 1]; +colidx = [6,4,2,2]; % color indices for different schemes + +P_launch = [0,3,6]; % input power sweep + +%% --- Create Figure --- + +fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); + +if isvalid(fig) + figure(fig) + % fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on + % t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact'); +else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on; grid on; + % t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact'); +end + +dsa = ["AVG"]; + +for d = 1 + + plotJob.dataStatArg = dsa(d); + cnt = 1; + + for l = 1:numel(Len) + + subplot(1,2,cnt); + cnt = cnt+1; + + for s = 1:length(P_launch) + + + for p = 1:numel(Title) + + plotJob.l = Len(l); + + plotJob.pol = Pol(p); + plotJob.d = D(p); + plotJob.sgm = Sgm(p); + + % color + style per length + baseColor = cols(colidx(p), :); + + if s == 1 + plotJob.linestyle = '-'; + elseif s == 2 + plotJob.linestyle = '--'; + else + plotJob.linestyle = ':'; + end + + + + if p == 1 + % plotJob.linestyle = '-'; + plotJob.markerstyle = 'o'; + plotJob.markersize = 2; + elseif p == 2 + % plotJob.linestyle = ':'; + plotJob.markerstyle = 'square'; + plotJob.markersize = 2; + elseif p == 3 + % plotJob.linestyle = '-'; + plotJob.markerstyle = 'x'; + plotJob.markersize = 6; + else + % plotJob.linestyle = '-'; + plotJob.markerstyle = 'diamond'; + plotJob.markersize = 2; + end + + % if d == 1 + % plotJob.linestyle = '-'; + % else + % plotJob.linestyle = ':'; + % plotJob.markerstyle = 'none'; + % end + + plotJob.p_in = P_launch(s); + + plotJob.displayname = sprintf('%s',Title(p)); + plotJob.color = baseColor;% * (1 - 0.15*(p-1)); % slight shade for powers + plotCurve(wh, plotJob); + % h = findobj(gca,'Type','Line','-not','Tag','FEC'); + % set(h(p),'DisplayName',sprintf('%s (%.0f km, %.0f dBm)',Title(p),Len(l),P_launch(s))); + % title(sprintf('%d km; %d Channels, \Delta f = %.0f GHz', ... + % plotJob.l, plotJob.ch, plotJob.channelspacing*1e-9)); + end + end + end +end +set(gca, 'YScale', 'log'); +xlabel(plotJob.xAxisLabel); +ylabel(plotJob.yAxisLabel); + +% legend('Interpreter','latex','NumColumns',2,'Location','southoutside'); +grid on; box on; + +copygraphics(fig, 'BackgroundColor','none'); diff --git a/Classes/Warehouse_class/functions/fwm_plots/dispersion_only.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/dispersion_only.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/dispersion_only.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/dispersion_only.m index a0e8a98..47e5566 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/dispersion_only.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/dispersion_only.m @@ -1,83 +1,83 @@ -%automate plots -[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\"); -wh = load([path filesep file]); -wh = wh.wh; - -plotJob = struct(); -width = 350; -height = 200; -plotJob.Position = [100 100 width 100+height]; -cols = cbrewer2("paired",12); -plotJob.color = cols(1,:); -plotJob.l = 1; -plotJob.ch = 1; - -plotJob.sgm = 1; -plotJob.pol = "copolarized"; -plotJob.p_in = 3; -plotJob.gamma = 0.0023; -plotJob.pmd = 0.1; -plotJob.channelspacing = 400e9; -plotJob.randzdw = 0; - - -plotJob.plot_ber_curve = 1; -plotJob.plot_3dber_curve = 0; -plotJob.plot_violin = 0; -plotJob.plot_wavelength_sweep = 0; -plotJob.plot_wavelength_sweep_failure_rate = 0; - - -plotJob.dataStatArg = 'Lineplot with quartiles'; -plotJob.plotTypeArg = 'Lines'; -plotJob.displayname = 'a'; -plotJob.title = 'title'; -plotJob.figName = '1 Chann__'; -plotJob.xAxisLabel = 'ROP per Channel in dBm'; -plotJob.yAxisLabel = 'BER'; - - -plotJob.d = 0; - -xAxis = wh.parameter.p_out.values; -D = wh.parameter.dispersion.values; - -figure() -ber_ = []; -for d_ = 0:39 - if d_ == 0 - plotJob.sgm = 0; - ber_(d_+1,:) = wh.getStoValue('ber',plotJob.l,d_,plotJob.sgm,string(plotJob.pol),plotJob.p_in,xAxis,plotJob.pmd,plotJob.gamma,1,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw)'; - else - plotJob.sgm = 1; - ber_(d_+1,:) = wh.getStoValue('ber',plotJob.l,d_,plotJob.sgm,string(plotJob.pol),plotJob.p_in,xAxis,plotJob.pmd,plotJob.gamma,1,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw)'; - end - hold on - plot(xAxis,ber_(d_+1,:)) - set(gca,'yscale','log'); -end -yline(3.8e-3); - - -hdfec = 3.8e-3.*ones(size(xAxis)); -for i = 1:size(ber_,1) - ber_series = ber_(i,:); - a = InterX([hdfec(:)';xAxis],[ber_series;xAxis]); - cross(i) = a(2); -end - -col = cbrewer2('Paired',8); -figure() -plot(D,cross,'Marker','o','MarkerSize',5,'MarkerEdgeColor',[1,1,1],'MarkerFaceColor',col(2,:),'Color',col(1,:),'LineWidth',1); -grid minor -xlabel('Accumulated Dispersion') -ylabel('Required ROP to reach FEC limit in dB') -line([D(16),D(16)],[-10,cross(16)],'linestyle','--') -line([0,D(16)],[cross(16),cross(16)],'linestyle','--') - -line([D(29),D(29)],[-10,cross(29)],'linestyle','--') -line([0,D(29)],[cross(29),cross(29)],'linestyle','--') - - - - +%automate plots +[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\"); +wh = load([path filesep file]); +wh = wh.wh; + +plotJob = struct(); +width = 350; +height = 200; +plotJob.Position = [100 100 width 100+height]; +cols = cbrewer2("paired",12); +plotJob.color = cols(1,:); +plotJob.l = 1; +plotJob.ch = 1; + +plotJob.sgm = 1; +plotJob.pol = "copolarized"; +plotJob.p_in = 3; +plotJob.gamma = 0.0023; +plotJob.pmd = 0.1; +plotJob.channelspacing = 400e9; +plotJob.randzdw = 0; + + +plotJob.plot_ber_curve = 1; +plotJob.plot_3dber_curve = 0; +plotJob.plot_violin = 0; +plotJob.plot_wavelength_sweep = 0; +plotJob.plot_wavelength_sweep_failure_rate = 0; + + +plotJob.dataStatArg = 'Lineplot with quartiles'; +plotJob.plotTypeArg = 'Lines'; +plotJob.displayname = 'a'; +plotJob.title = 'title'; +plotJob.figName = '1 Chann__'; +plotJob.xAxisLabel = 'ROP per Channel in dBm'; +plotJob.yAxisLabel = 'BER'; + + +plotJob.d = 0; + +xAxis = wh.parameter.p_out.values; +D = wh.parameter.dispersion.values; + +figure() +ber_ = []; +for d_ = 0:39 + if d_ == 0 + plotJob.sgm = 0; + ber_(d_+1,:) = wh.getStoValue('ber',plotJob.l,d_,plotJob.sgm,string(plotJob.pol),plotJob.p_in,xAxis,plotJob.pmd,plotJob.gamma,1,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw)'; + else + plotJob.sgm = 1; + ber_(d_+1,:) = wh.getStoValue('ber',plotJob.l,d_,plotJob.sgm,string(plotJob.pol),plotJob.p_in,xAxis,plotJob.pmd,plotJob.gamma,1,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw)'; + end + hold on + plot(xAxis,ber_(d_+1,:)) + set(gca,'yscale','log'); +end +yline(3.8e-3); + + +hdfec = 3.8e-3.*ones(size(xAxis)); +for i = 1:size(ber_,1) + ber_series = ber_(i,:); + a = InterX([hdfec(:)';xAxis],[ber_series;xAxis]); + cross(i) = a(2); +end + +col = cbrewer2('Paired',8); +figure() +plot(D,cross,'Marker','o','MarkerSize',5,'MarkerEdgeColor',[1,1,1],'MarkerFaceColor',col(2,:),'Color',col(1,:),'LineWidth',1); +grid minor +xlabel('Accumulated Dispersion') +ylabel('Required ROP to reach FEC limit in dB') +line([D(16),D(16)],[-10,cross(16)],'linestyle','--') +line([0,D(16)],[cross(16),cross(16)],'linestyle','--') + +line([D(29),D(29)],[-10,cross(29)],'linestyle','--') +line([0,D(29)],[cross(29),cross(29)],'linestyle','--') + + + + diff --git a/Classes/Warehouse_class/functions/fwm_plots/dispersion_validation_miniskript.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/dispersion_validation_miniskript.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/dispersion_validation_miniskript.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/dispersion_validation_miniskript.m index 802db0a..e0d6843 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/dispersion_validation_miniskript.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/dispersion_validation_miniskript.m @@ -1,52 +1,52 @@ - -wh = load("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_dispersion_validation\wh_with_variation.mat"); -wh = wh.wh; - -lambda = 1295; - -figure(3) -plot(xAxis,getber(wh,lambda),'DisplayName',['w:', num2str(lambda), 'variation']); -yline(3.8e-3,'HandleVisibility','off'); -legend -set(gca,'yscale','log'); -grid(gca,'on'); -grid(gca,'minor'); -grid minor -fontsize(gca,8,"points") -fig.Units = "centimeters"; -fig.Position = [2 2 8.5 7]; -set(gca,'TickLabelInterpreter','latex') -ylim([1e-5,0.5]); -xlim([min(xAxis),-3]); - -wh = load("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_dispersion_validation\wh_no_variation.mat"); -wh = wh.wh; - - -figure(3) -hold on -plot(xAxis,getber(wh,lambda),'DisplayName',['w:', num2str(lambda), 'no variation']); -yline(3.8e-3,'HandleVisibility','off'); -legend -set(gca,'yscale','log'); -grid(gca,'on'); -grid(gca,'minor'); -grid minor -fontsize(gca,8,"points") -fig.Units = "centimeters"; -fig.Position = [2 2 8.5 7]; -set(gca,'TickLabelInterpreter','latex') -ylim([1e-5,0.5]); -xlim([min(xAxis),-3]); - - -function ber = getber(wh,lambda) - realization = wh.parameter.realization.values(1:end); - xAxis = wh.parameter.p_out.values; - ber = []; - for xl = 1:numel(xAxis) - p_out = xAxis(xl); - temp = wh.getStoValue('ber',10,0,0,"copolarized",3,p_out,0.1,0.0023,realization,1,lambda,400e9,1); - ber(xl) = mean(temp,'all'); - end + +wh = load("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_dispersion_validation\wh_with_variation.mat"); +wh = wh.wh; + +lambda = 1295; + +figure(3) +plot(xAxis,getber(wh,lambda),'DisplayName',['w:', num2str(lambda), 'variation']); +yline(3.8e-3,'HandleVisibility','off'); +legend +set(gca,'yscale','log'); +grid(gca,'on'); +grid(gca,'minor'); +grid minor +fontsize(gca,8,"points") +fig.Units = "centimeters"; +fig.Position = [2 2 8.5 7]; +set(gca,'TickLabelInterpreter','latex') +ylim([1e-5,0.5]); +xlim([min(xAxis),-3]); + +wh = load("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_dispersion_validation\wh_no_variation.mat"); +wh = wh.wh; + + +figure(3) +hold on +plot(xAxis,getber(wh,lambda),'DisplayName',['w:', num2str(lambda), 'no variation']); +yline(3.8e-3,'HandleVisibility','off'); +legend +set(gca,'yscale','log'); +grid(gca,'on'); +grid(gca,'minor'); +grid minor +fontsize(gca,8,"points") +fig.Units = "centimeters"; +fig.Position = [2 2 8.5 7]; +set(gca,'TickLabelInterpreter','latex') +ylim([1e-5,0.5]); +xlim([min(xAxis),-3]); + + +function ber = getber(wh,lambda) + realization = wh.parameter.realization.values(1:end); + xAxis = wh.parameter.p_out.values; + ber = []; + for xl = 1:numel(xAxis) + p_out = xAxis(xl); + temp = wh.getStoValue('ber',10,0,0,"copolarized",3,p_out,0.1,0.0023,realization,1,lambda,400e9,1); + ber(xl) = mean(temp,'all'); + end end \ No newline at end of file diff --git a/Classes/Warehouse_class/functions/fwm_plots/generatePlots.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/generatePlots.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/generatePlots.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/generatePlots.m index 2e720ff..911b79e 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/generatePlots.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/generatePlots.m @@ -1,61 +1,61 @@ -function generatePlots(wh,plotJob) - -% 0) Test for valid query: -p_out = wh.parameter.p_out.values(1); -realization = 9; - - -if 1 %~isempty(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310)) - % test violin - - baseName = plotJob.figName; - - width = 350; - height = 200; - s = 100; - e = 100; - - if plotJob.plot_ber_curve - plotJob.Position = [100 100 width e+height]; - plotJob.figName = [baseName, ' zdwvsber']; - plotCurve(wh, plotJob); - end - - if plotJob.plot_3dber_curve - plotJob.Position = [100 100 width e+height]; - plotJob.figName = [baseName, ' zdwvsber']; - plot3dCurve(wh, plotJob); - end - - if plotJob.plot_wavelength_sweep - plotJob.Position = [100 100 width e+height]; - plotJob.figName = [baseName, ' zdwvsber']; - plotBerVsZDW(wh, plotJob); - end - - if plotJob.plot_wavelength_sweep_failure_rate - plotJob.Position = [100 100 width e+height]; - plotJob.figName = [baseName, ' zdwvsber']; - plotBerVsZdwFailureRate(wh, plotJob); - end - - if plotJob.plot_violin - plotJob.Position = [s+width 100 width e+height]; - plotJob.figName = [baseName, ' violin']; - plotViolin(wh, plotJob); - end - - - if 0 - %2) plotHistogram - plotJob.Position = [s+2*width 100 width e+height]; - plotJob.figName = [baseName, ' FEC crossing']; - plotHistogram(wh,plotJob) - end - - -else - warndlg('The requested Datapoint is not available... This can occur for some edgecase constellations... ') -end - +function generatePlots(wh,plotJob) + +% 0) Test for valid query: +p_out = wh.parameter.p_out.values(1); +realization = 9; + + +if 1 %~isempty(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310)) + % test violin + + baseName = plotJob.figName; + + width = 350; + height = 200; + s = 100; + e = 100; + + if plotJob.plot_ber_curve + plotJob.Position = [100 100 width e+height]; + plotJob.figName = [baseName, ' zdwvsber']; + plotCurve(wh, plotJob); + end + + if plotJob.plot_3dber_curve + plotJob.Position = [100 100 width e+height]; + plotJob.figName = [baseName, ' zdwvsber']; + plot3dCurve(wh, plotJob); + end + + if plotJob.plot_wavelength_sweep + plotJob.Position = [100 100 width e+height]; + plotJob.figName = [baseName, ' zdwvsber']; + plotBerVsZDW(wh, plotJob); + end + + if plotJob.plot_wavelength_sweep_failure_rate + plotJob.Position = [100 100 width e+height]; + plotJob.figName = [baseName, ' zdwvsber']; + plotBerVsZdwFailureRate(wh, plotJob); + end + + if plotJob.plot_violin + plotJob.Position = [s+width 100 width e+height]; + plotJob.figName = [baseName, ' violin']; + plotViolin(wh, plotJob); + end + + + if 0 + %2) plotHistogram + plotJob.Position = [s+2*width 100 width e+height]; + plotJob.figName = [baseName, ' FEC crossing']; + plotHistogram(wh,plotJob) + end + + +else + warndlg('The requested Datapoint is not available... This can occur for some edgecase constellations... ') +end + end \ No newline at end of file diff --git a/Classes/Warehouse_class/functions/fwm_plots/plot3dCurve.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plot3dCurve.m similarity index 97% rename from Classes/Warehouse_class/functions/fwm_plots/plot3dCurve.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plot3dCurve.m index 37d8fcf..5929aa6 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plot3dCurve.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plot3dCurve.m @@ -1,248 +1,248 @@ -function plotCurve(wh,plotJob) -%PLOTCURVE Summary of this function goes here -% Detailed explanation goes here - -fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); - -if isvalid(fig) - figure(fig) - fig = get(fig); - AxesMain = fig.CurrentAxes; - hold on -else - fig = figure('name',char(plotJob.figName)); - AxesMain = gca; - hold on -end - -col = plotJob.color; - -% we want to fetch all realizations -if plotJob.pmd == 0 - realization = 499; - % realization = 0:7; -else - realization = wh.parameter.realization.values(1:end); -end -realization = wh.parameter.realization.values(1:end); -% get all xAxis values -xAxis = wh.parameter.p_out.values; - -% Fetch Data from Warehouse -for xl = 1:numel(xAxis) - p_out = xAxis(xl); - if string(plotJob.dataStatArg) == "Worst" - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - ber(xl) = max(temp,[],'all'); - linew = 1.0; - markersz = 3; - linestyle = '-'; - elseif string(plotJob.dataStatArg) == "AVG" - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - ber(xl) = mean(temp,'all'); - linew = 1.0; - markersz = 3; - linestyle = '-'; - elseif string(plotJob.dataStatArg) == "Best" - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - ber(xl) = min(temp,[],'all'); - linew = 1.0; - markersz = 3; - linestyle = '-'; - - elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)" - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - - ber(:,xl) = mean(temp,1,"omitnan").'; - - linew = 1; - markersz = 3; - linestyle = '--'; - - elseif string(plotJob.dataStatArg) == "Lineplot with quartiles" - - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - ber(xl) = mean(temp,"all","omitnan").'; - - - upperq(xl) = quantile(temp,0.9,"all"); - lowerq(xl) = quantile(temp,0.1,"all"); - - if lowerq(xl) == 0 - lowerq(xl) = lowerq(xl-1); - end - % upperq(xl) = 0.5*std(tmp,1,'all','omitnan'); - % lowerq(xl) = 0.5*std(tmp,1,'all','omitnan'); - - % upperq(xl) = max(dataNoNans); - % lowerq(xl) = min(dataNoNans); - - % upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp))); - % lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp))); - - linew = 1; - markersz = 1; - linestyle = '-'; - - elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations" - - tmp = reshape(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).',[],1); - ber(1:size(tmp,1),xl) = tmp; - - linew = 0.3; - markersz = 2; - linestyle = ':'; - end -end - -xAxis = xAxis; - -% Plot Data -if string(plotJob.plotTypeArg) == "Scatter" - for rlz = 1:size(ber,1) - - if rlz < size(ber,1) - scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'HandleVisibility','off'); - else - scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); - end - - end - -elseif string(plotJob.plotTypeArg) == "Lines" - - % if ~anynan(ber) - % [xAxis,ber] = interpCurve(xAxis, ber); - % end - - for rlz = 1:size(ber,1) -% - if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)") - ch = mod(rlz,plotJob.ch); - if ch == 0; ch = plotJob.ch; end - else - ch = plotJob.dataStatArg; - end - - if rlz <= size(ber,1) - - - s = plot3(xAxis,repmat(ch,1,numel(xAxis)),ber(rlz,:),linestyle,'Marker',"o",'MarkerSize',markersz,'MarkerFaceColor',plotJob.color,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off'); - - s.DataTipTemplate.Interpreter = "latex"; - s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; - s.DataTipTemplate.DataTipRows(1).Value = repmat(ch,size(ber)); - s.DataTipTemplate.DataTipRows(1); - s.DataTipTemplate.DataTipRows(2) = []; - - - else - - if string(plotJob.dataStatArg) == "Lineplot with quartiles" - [hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-*', 'alpha','Color',col,'transparency', 0.2); - hp.LineWidth = 1.2; - - ho = outlinebounds(hl,hp); - set(ho, 'linestyle', ':', 'color', col); - else - - s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"o",'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]); - - s.DataTipTemplate.Interpreter = "latex"; - s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; - s.DataTipTemplate.DataTipRows(1).Value = repmat(string(ch),size(ber)); - s.DataTipTemplate.DataTipRows(1) - s.DataTipTemplate.DataTipRows(2) = []; - end - - end - - end -end - -% Draw FEC Threshold Line -%get x data of first children: -%get all linear Values -if 0 - linear = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,wh.parameter.p_out.values,0,0,499,"symmetric"); - linear = mean(linear,2); - lincurve = findall(AxesMain, 'Type', 'line','DisplayName','Linear Baseline'); - % - if isempty(lincurve) - xdata = AxesMain.Children(1).XData; - hdfec = 3.8e-3.*ones(size(xdata)); - plot(xdata,linear,'-','Marker',"o",'MarkerSize',3,'LineWidth',4,'Color',[0.6400 0.6400 0.6400],'MarkerFaceColor',[0.6400 0.6400 0.6400],'Parent', AxesMain,'DisplayName','Linear Baseline'); - %h = get(gca,'Children'); - %set(gca,'Children',[h(2) h(1)]) - end -end - -feccurve = findall(AxesMain, 'Type', 'line','DisplayName','FEC $3.8*10^{-3}$'); -% -if isempty(feccurve) - xdata = xAxis; - hdfec = 3.8e-3.*ones(size(xdata)); - for ch = 1:plotJob.ch - plot3(xdata,repmat(ch,1,numel(xAxis)),hdfec,':','MarkerSize',4,'Color','black','MarkerFaceColor','black','LineWidth',0.5,'Parent', AxesMain,'DisplayName','FEC $3.8*10^{-3}$','HandleVisibility','off'); - end - %h = get(gca,'Children'); - %set(gca,'Children',[h(2) h(1)]) -end - -% Figure Settings -%title(AxesMain,plotJob.title,"Interpreter","none"); - -xlabel(AxesMain,plotJob.xAxisLabel,"Interpreter","none"); - -ylabel(AxesMain,plotJob.yAxisLabel,"Interpreter","none"); - -set(AxesMain,'zscale','log'); - -grid(AxesMain,'on'); - -grid(AxesMain,'minor'); - -grid minor - -view(AxesMain,[42.0619302949062 23.4176470588235]); -%legend(AxesMain); - -fontsize(AxesMain,8,"points") -fontname(AxesMain,"Arial") - -fig.Position = plotJob.Position; -fig.Units = "centimeters"; -fig.Position = [2 2 8.5 7]; - -set(AxesMain,'TickLabelInterpreter','none') - -set(AxesMain.Legend,'Interpreter','none') -% set(gcf,'Units','centimeters') -% set(gcf,'Position',[2 2 9 4.5]) - -zlim([1e-4,0.3]); - -xlim([min(xAxis),-3]); - - -annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points") - - -hold off - - -end - - -function vec = removeZeros(vec) - % Find rows that contain only zeros - rows_to_remove = all(vec == 0, 2); - - % Remove rows with only zeros - vec(rows_to_remove, :) = []; -end +function plotCurve(wh,plotJob) +%PLOTCURVE Summary of this function goes here +% Detailed explanation goes here + +fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); + +if isvalid(fig) + figure(fig) + fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on +else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on +end + +col = plotJob.color; + +% we want to fetch all realizations +if plotJob.pmd == 0 + realization = 499; + % realization = 0:7; +else + realization = wh.parameter.realization.values(1:end); +end +realization = wh.parameter.realization.values(1:end); +% get all xAxis values +xAxis = wh.parameter.p_out.values; + +% Fetch Data from Warehouse +for xl = 1:numel(xAxis) + p_out = xAxis(xl); + if string(plotJob.dataStatArg) == "Worst" + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + ber(xl) = max(temp,[],'all'); + linew = 1.0; + markersz = 3; + linestyle = '-'; + elseif string(plotJob.dataStatArg) == "AVG" + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + ber(xl) = mean(temp,'all'); + linew = 1.0; + markersz = 3; + linestyle = '-'; + elseif string(plotJob.dataStatArg) == "Best" + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + ber(xl) = min(temp,[],'all'); + linew = 1.0; + markersz = 3; + linestyle = '-'; + + elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)" + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + + ber(:,xl) = mean(temp,1,"omitnan").'; + + linew = 1; + markersz = 3; + linestyle = '--'; + + elseif string(plotJob.dataStatArg) == "Lineplot with quartiles" + + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + ber(xl) = mean(temp,"all","omitnan").'; + + + upperq(xl) = quantile(temp,0.9,"all"); + lowerq(xl) = quantile(temp,0.1,"all"); + + if lowerq(xl) == 0 + lowerq(xl) = lowerq(xl-1); + end + % upperq(xl) = 0.5*std(tmp,1,'all','omitnan'); + % lowerq(xl) = 0.5*std(tmp,1,'all','omitnan'); + + % upperq(xl) = max(dataNoNans); + % lowerq(xl) = min(dataNoNans); + + % upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp))); + % lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp))); + + linew = 1; + markersz = 1; + linestyle = '-'; + + elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations" + + tmp = reshape(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).',[],1); + ber(1:size(tmp,1),xl) = tmp; + + linew = 0.3; + markersz = 2; + linestyle = ':'; + end +end + +xAxis = xAxis; + +% Plot Data +if string(plotJob.plotTypeArg) == "Scatter" + for rlz = 1:size(ber,1) + + if rlz < size(ber,1) + scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'HandleVisibility','off'); + else + scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); + end + + end + +elseif string(plotJob.plotTypeArg) == "Lines" + + % if ~anynan(ber) + % [xAxis,ber] = interpCurve(xAxis, ber); + % end + + for rlz = 1:size(ber,1) +% + if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)") + ch = mod(rlz,plotJob.ch); + if ch == 0; ch = plotJob.ch; end + else + ch = plotJob.dataStatArg; + end + + if rlz <= size(ber,1) + + + s = plot3(xAxis,repmat(ch,1,numel(xAxis)),ber(rlz,:),linestyle,'Marker',"o",'MarkerSize',markersz,'MarkerFaceColor',plotJob.color,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off'); + + s.DataTipTemplate.Interpreter = "latex"; + s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; + s.DataTipTemplate.DataTipRows(1).Value = repmat(ch,size(ber)); + s.DataTipTemplate.DataTipRows(1); + s.DataTipTemplate.DataTipRows(2) = []; + + + else + + if string(plotJob.dataStatArg) == "Lineplot with quartiles" + [hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-*', 'alpha','Color',col,'transparency', 0.2); + hp.LineWidth = 1.2; + + ho = outlinebounds(hl,hp); + set(ho, 'linestyle', ':', 'color', col); + else + + s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"o",'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]); + + s.DataTipTemplate.Interpreter = "latex"; + s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; + s.DataTipTemplate.DataTipRows(1).Value = repmat(string(ch),size(ber)); + s.DataTipTemplate.DataTipRows(1) + s.DataTipTemplate.DataTipRows(2) = []; + end + + end + + end +end + +% Draw FEC Threshold Line +%get x data of first children: +%get all linear Values +if 0 + linear = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,wh.parameter.p_out.values,0,0,499,"symmetric"); + linear = mean(linear,2); + lincurve = findall(AxesMain, 'Type', 'line','DisplayName','Linear Baseline'); + % + if isempty(lincurve) + xdata = AxesMain.Children(1).XData; + hdfec = 3.8e-3.*ones(size(xdata)); + plot(xdata,linear,'-','Marker',"o",'MarkerSize',3,'LineWidth',4,'Color',[0.6400 0.6400 0.6400],'MarkerFaceColor',[0.6400 0.6400 0.6400],'Parent', AxesMain,'DisplayName','Linear Baseline'); + %h = get(gca,'Children'); + %set(gca,'Children',[h(2) h(1)]) + end +end + +feccurve = findall(AxesMain, 'Type', 'line','DisplayName','FEC $3.8*10^{-3}$'); +% +if isempty(feccurve) + xdata = xAxis; + hdfec = 3.8e-3.*ones(size(xdata)); + for ch = 1:plotJob.ch + plot3(xdata,repmat(ch,1,numel(xAxis)),hdfec,':','MarkerSize',4,'Color','black','MarkerFaceColor','black','LineWidth',0.5,'Parent', AxesMain,'DisplayName','FEC $3.8*10^{-3}$','HandleVisibility','off'); + end + %h = get(gca,'Children'); + %set(gca,'Children',[h(2) h(1)]) +end + +% Figure Settings +%title(AxesMain,plotJob.title,"Interpreter","none"); + +xlabel(AxesMain,plotJob.xAxisLabel,"Interpreter","none"); + +ylabel(AxesMain,plotJob.yAxisLabel,"Interpreter","none"); + +set(AxesMain,'zscale','log'); + +grid(AxesMain,'on'); + +grid(AxesMain,'minor'); + +grid minor + +view(AxesMain,[42.0619302949062 23.4176470588235]); +%legend(AxesMain); + +fontsize(AxesMain,8,"points") +fontname(AxesMain,"Arial") + +fig.Position = plotJob.Position; +fig.Units = "centimeters"; +fig.Position = [2 2 8.5 7]; + +set(AxesMain,'TickLabelInterpreter','none') + +set(AxesMain.Legend,'Interpreter','none') +% set(gcf,'Units','centimeters') +% set(gcf,'Position',[2 2 9 4.5]) + +zlim([1e-4,0.3]); + +xlim([min(xAxis),-3]); + + +annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points") + + +hold off + + +end + + +function vec = removeZeros(vec) + % Find rows that contain only zeros + rows_to_remove = all(vec == 0, 2); + + % Remove rows with only zeros + vec(rows_to_remove, :) = []; +end diff --git a/Classes/Warehouse_class/functions/fwm_plots/plotBerVsZDW.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotBerVsZDW.m similarity index 97% rename from Classes/Warehouse_class/functions/fwm_plots/plotBerVsZDW.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotBerVsZDW.m index bebc5b5..d0d5f06 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotBerVsZDW.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotBerVsZDW.m @@ -1,300 +1,300 @@ -function plotBerVsZDW(wh,plotJob) - - fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); - - if isvalid(fig) - figure(fig) - fig = get(fig); - AxesMain = fig.CurrentAxes; - hold on - else - fig = figure('name',char(plotJob.figName)); - AxesMain = gca; - hold on - end - - - col = plotJob.color; - - % we want to fetch all realizations - realization = wh.parameter.realization.values(1:end); - - % get all xAxis values - xAxis = wh.parameter.p_out.values; - - % get all center wavelengths - wavelengths = wh.parameter.center_wavelength.values; - -% totber = NaN(numel(realization),1,numel(xAxis),numel(wavelengths)); -% ber = zeros(numel(realization),plotJob.ch,numel(xAxis),numel(wavelengths)); - - %get BER values for query - for w = 2:numel(wavelengths) - - for xl = 1:numel(xAxis) - - c_wavelen = wavelengths(w); - p_out = xAxis(xl); - - % dim1 : realiz; dim2: channels, dim3: rop, dim4, c_wavelength - temp = removeZeros(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,c_wavelen,plotJob.channelspacing,plotJob.randzdw)); - ber(1:size(temp,1),:,xl,w) = temp; - - end - end - - hdfec = 3.8e-3.*ones(size(xAxis)); - zdw_ = []; - zdw_chann = []; - zdw_tot = []; - cf_tot = []; - cf_ = []; - S = []; - Stot = []; - S_chann = []; - cf_chann = []; - - cnt = 0; - %get fec thresholds - %linear = squeeze(linear); - for c_wavelen = 1:size(ber,4) - for realiz = 1:size(ber,1) - for chann = 1:size(ber,2) - - %finde Schnittpunkt zwischen FEC und BER Kurve - temp_ber = squeeze(ber(realiz,chann,:,c_wavelen)).'; - if ~all(temp_ber == 0) - %nur wenn nicht alles nullen sind - crossing_ch = InterX([hdfec;xAxis],[temp_ber;xAxis]); - else - continue - end - - %Req. FEC Ergebnis einsortieren - if ~isempty(crossing_ch) - if crossing_ch(2) == 0 - print("d") - end - S(realiz,chann,c_wavelen) = crossing_ch(2); - else - S(realiz,chann,c_wavelen) = -1; - cnt = cnt +1; - end - - - end - end - end - - temp_max = -inf; - for i = 1:plotJob.ch - hold on - %S:: 1.dim: realiz; 2.dim: channel; 3.dim: center wavelength - %squeeze a channel: - temp_data = squeeze(S(:,i,:)); - - %remove realizations that have no entry (only zero) - temp_data = removeZeros(temp_data); - - %replace zeros with NAN (e.g. for the wavelengths that have missing realizations) - temp_data(temp_data==0) = NaN; - - %plot required ROP for channel and all realizations that cross the - %FEC limit - scatter(wavelengths,temp_data ,5,plotJob.color,'Marker','.'); - -% %plot mean per channel -% temp_mean = mean(temp_data,'omitnan'); -% hold on -% plot(wavelengths,temp_mean,'Marker','*'); - - %get max overall value - temp_max = max(temp_max,max(temp_data)); - end - - - - %plot mean overall - mean_overall = squeeze(mean(S,2)); - mean_overall(mean_overall==0) = NaN; - %mean_overall(mean_overall==-1) = NaN; - mean_overall=mean(mean_overall,1,'omitnan'); - plot(wavelengths,mean_overall,'Color',plotJob.color); - - %plot max overall - scatter(wavelengths,temp_max ,35,plotJob.color,'Marker','v'); - - - %plot channel positions - hold on - chpos = calcWavelengthPlan(plotJob.ch, 400e9, 1310); - xline(chpos,'LineWidth',2,'Alpha',0.2); - chpos = calcWavelengthPlan(plotJob.ch, 400e9, chpos(4)); - xline(chpos,'LineWidth',2,'Alpha',0.2); - - fig.Position = plotJob.Position; - - ylabel('Penalty in dB'); - xlabel('Wavelength in nm'); - - xlim([min(wavelengths),max(wavelengths) ]); - - grid minor; - set(gca, 'color', 'none'); - legend = []; - - fontsize(AxesMain,8,"points") - - fig.Position = plotJob.Position; - fig.Units = "centimeters"; - fig.Position = [2 2 8.5 7]; - - set(AxesMain,'TickLabelInterpreter','latex') - - set(AxesMain.Legend,'Interpreter','latex') - - - - - - - % - % - % - % - % - % distinct_cf = unique(cf_chann); - % - % for i = 1:length(distinct_cf) - % indices = find(cf_chann==distinct_cf(i)); - % cf(i) = distinct_cf(i); - % worst_fec_cross(i) = max(S_chann(indices)); - % avg_fec_cross(i) = mean(S_chann(indices)); - % end - % - % avg_fec_cross = smooth(avg_fec_cross,5); - % - % figure(224) - % hold on - % scatter(cf_,S,10.*abs(S-mean(S)).*ones(size(S)),'DisplayName',['AVG'],'MarkerEdgeColor',col,'MarkerFaceColor',col,'Marker','.'); - % hold on - % scatter(cf(2:end),worst_fec_cross(2:end),15,'DisplayName',['Worst'],'MarkerEdgeColor',col,'MarkerFaceColor',col,'Marker','.','HandleVisibility','off'); - % plot(cf(2:end),avg_fec_cross(2:end),'DisplayName',['AVG'],'LineWidth',1,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2); - % plot(cf(2:end),worst_fec_cross(2:end),'DisplayName',['AVG'],'LineWidth',0.5,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2); - % ghzgrid = hz2nm(nm2hz(1310)+[0:1:20].*400e9); - % set(gca,'xtick',sort(ghzgrid)) - % xlim([min(cf(cf~=0)), 1310.1]); - % - % - % % With matlab internal errorbar function... - % figure(221) - % %plot(cf,avg_fec_cross,'DisplayName',['AVG'],'LineWidth',1,'Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2); - % hold on - % %plot(cf_tot,min(S_chann(1:length(cf_tot),:),[],2),'DisplayName',['AVG'],'LineWidth',1,'LineStyle',':','Color',col,'Marker','^','MarkerFaceColor',col,'MarkerSize',2); - % plot(cf,worst_fec_cross,'DisplayName',['AVG'],'LineWidth',2,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2); - % ghzgrid = hz2nm(nm2hz(1310)+[0:1:20].*400e9); - % set(gca,'xtick',sort(ghzgrid)) - % xlim([1302, 1310.1]); - % xline(ghzgrid,'LineStyle',':','Color',[.7 .7 .7]); - - - %with - % Stot = movmean(Stot,5); - % figure(222) - % [hl,hp] = boundedline(cf_tot,Stot,[(Stot'-min(S_chann(1:length(cf_tot),:),[],2)),(max(S_chann(1:length(cf_tot),:),[],2)-Stot')], 'alpha','Color',col,'transparency', 0.05); - % ho = outlinebounds(hl,hp); - % set(ho, 'linestyle', ':', 'color', col, 'marker', '.','linewidth',0.5); - % hold on - % - % ghzgrid = hz2nm(nm2hz(1310)+[0:1:12].*400e9); - % xline(ghzgrid); - - - - - %plot the total ber - - % %figure(22); - % hold on; - % b= movmean(Stot,3); - % plot(AxesMain,cf_tot,b,'LineWidth',2,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname],'Color',col,'Marker','o'); - % hold on - - - - - - %scatter(AxesMain,zdw_,S,'Marker','+','MarkerEdgeColor',col,'MarkerFaceAlpha',0.4,'MarkerEdgeAlpha',0.4,'LineWidth',0.5,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); - %ylim(AxesMain,[-9.3 -7]); - - % for i = 1:numel(chp) - % hold on - % xline(AxesMain,chp(i),'Color',colr(i,:),'DisplayName',['CH: ', num2str(i)],'LineWidth',1.5); - % hold off - % end - - - - - % - % a = movmean(sortrows([zdw_; S]'),10,'Endpoints','discard'); - % - % sorted = sortrows([zdw_; S]'); - % figure(2) - % scatter(sorted(:,1),sorted(:,2)) - % - % ber_sorted = sort(S); - % mean(ber_sorted); - % std(ber_sorted); - % z1 = []; - % penalty = mean(ber_sorted):0.01:mean(ber_sorted)+2; - % for i = 1:length(penalty) - % l = penalty(i); - % if i == 1 - % z1 = [z1 sum(ber_sorted(1,:)l-0.01) / length(ber_sorted) ]; - % end - % - % end - % - % penalty_higherthan = 0.5; - % probability = sum(z1(find(penalty>mean(ber_sorted)+penalty_higherthan))); - % disp(['A penalty of more than 1dB has a probability of: ', num2str(probability)]); - % % - % stem(AxesMain,penalty,z1,"filled",'Marker','o','MarkerSize',2,'Color',col); - % - % - % [f1,x1]=ecdf(S(end,:)); - % %figure(23);plot(AxesMain,x1,f1,'r','LineWidth',3, 'Color',col); - % - % %plot(AxesMain,a(:,1),a(:,2),'Color',col+1,'Parent', AxesMain(1)); - % - % % histogram(AxesMain,S,1000,'EdgeColor','none','FaceAlpha',0.4); - % - % - % - % % - % %scatter(AxesMain,zdw_,S,'Marker','+','MarkerEdgeColor',col,'MarkerFaceAlpha',0.4,'MarkerEdgeAlpha',0.4,'LineWidth',0.5,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); - % hold on - % %scatter(AxesMain,zdw_chann(:,1),mean(S_chann,2),'Marker','diamond','MarkerEdgeColor',col,'MarkerFaceAlpha',0.6,'LineWidth',7,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); - % hold off - % % - % % for rlz = 1:size(S,2) - % % zdwval = zdw_(rlz); - % % feccrossing = S(rlz); - % % scatter(AxesMain,zdwval,feccrossing,10,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); - % % end - % - % xline([1302.03471732576,1304.30061112288,1306.57440520904,1308.85614097425,1311.14586009814,1313.44360455251,1315.74941660391,1318.06333881618]); - - -end - -function vec = removeZeros(vec) - % Find rows that contain only zeros - rows_to_remove = all(vec == 0, 2); - - % Remove rows with only zeros - vec(rows_to_remove, :) = []; +function plotBerVsZDW(wh,plotJob) + + fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); + + if isvalid(fig) + figure(fig) + fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on + else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on + end + + + col = plotJob.color; + + % we want to fetch all realizations + realization = wh.parameter.realization.values(1:end); + + % get all xAxis values + xAxis = wh.parameter.p_out.values; + + % get all center wavelengths + wavelengths = wh.parameter.center_wavelength.values; + +% totber = NaN(numel(realization),1,numel(xAxis),numel(wavelengths)); +% ber = zeros(numel(realization),plotJob.ch,numel(xAxis),numel(wavelengths)); + + %get BER values for query + for w = 2:numel(wavelengths) + + for xl = 1:numel(xAxis) + + c_wavelen = wavelengths(w); + p_out = xAxis(xl); + + % dim1 : realiz; dim2: channels, dim3: rop, dim4, c_wavelength + temp = removeZeros(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,c_wavelen,plotJob.channelspacing,plotJob.randzdw)); + ber(1:size(temp,1),:,xl,w) = temp; + + end + end + + hdfec = 3.8e-3.*ones(size(xAxis)); + zdw_ = []; + zdw_chann = []; + zdw_tot = []; + cf_tot = []; + cf_ = []; + S = []; + Stot = []; + S_chann = []; + cf_chann = []; + + cnt = 0; + %get fec thresholds + %linear = squeeze(linear); + for c_wavelen = 1:size(ber,4) + for realiz = 1:size(ber,1) + for chann = 1:size(ber,2) + + %finde Schnittpunkt zwischen FEC und BER Kurve + temp_ber = squeeze(ber(realiz,chann,:,c_wavelen)).'; + if ~all(temp_ber == 0) + %nur wenn nicht alles nullen sind + crossing_ch = InterX([hdfec;xAxis],[temp_ber;xAxis]); + else + continue + end + + %Req. FEC Ergebnis einsortieren + if ~isempty(crossing_ch) + if crossing_ch(2) == 0 + print("d") + end + S(realiz,chann,c_wavelen) = crossing_ch(2); + else + S(realiz,chann,c_wavelen) = -1; + cnt = cnt +1; + end + + + end + end + end + + temp_max = -inf; + for i = 1:plotJob.ch + hold on + %S:: 1.dim: realiz; 2.dim: channel; 3.dim: center wavelength + %squeeze a channel: + temp_data = squeeze(S(:,i,:)); + + %remove realizations that have no entry (only zero) + temp_data = removeZeros(temp_data); + + %replace zeros with NAN (e.g. for the wavelengths that have missing realizations) + temp_data(temp_data==0) = NaN; + + %plot required ROP for channel and all realizations that cross the + %FEC limit + scatter(wavelengths,temp_data ,5,plotJob.color,'Marker','.'); + +% %plot mean per channel +% temp_mean = mean(temp_data,'omitnan'); +% hold on +% plot(wavelengths,temp_mean,'Marker','*'); + + %get max overall value + temp_max = max(temp_max,max(temp_data)); + end + + + + %plot mean overall + mean_overall = squeeze(mean(S,2)); + mean_overall(mean_overall==0) = NaN; + %mean_overall(mean_overall==-1) = NaN; + mean_overall=mean(mean_overall,1,'omitnan'); + plot(wavelengths,mean_overall,'Color',plotJob.color); + + %plot max overall + scatter(wavelengths,temp_max ,35,plotJob.color,'Marker','v'); + + + %plot channel positions + hold on + chpos = calcWavelengthPlan(plotJob.ch, 400e9, 1310); + xline(chpos,'LineWidth',2,'Alpha',0.2); + chpos = calcWavelengthPlan(plotJob.ch, 400e9, chpos(4)); + xline(chpos,'LineWidth',2,'Alpha',0.2); + + fig.Position = plotJob.Position; + + ylabel('Penalty in dB'); + xlabel('Wavelength in nm'); + + xlim([min(wavelengths),max(wavelengths) ]); + + grid minor; + set(gca, 'color', 'none'); + legend = []; + + fontsize(AxesMain,8,"points") + + fig.Position = plotJob.Position; + fig.Units = "centimeters"; + fig.Position = [2 2 8.5 7]; + + set(AxesMain,'TickLabelInterpreter','latex') + + set(AxesMain.Legend,'Interpreter','latex') + + + + + + + % + % + % + % + % + % distinct_cf = unique(cf_chann); + % + % for i = 1:length(distinct_cf) + % indices = find(cf_chann==distinct_cf(i)); + % cf(i) = distinct_cf(i); + % worst_fec_cross(i) = max(S_chann(indices)); + % avg_fec_cross(i) = mean(S_chann(indices)); + % end + % + % avg_fec_cross = smooth(avg_fec_cross,5); + % + % figure(224) + % hold on + % scatter(cf_,S,10.*abs(S-mean(S)).*ones(size(S)),'DisplayName',['AVG'],'MarkerEdgeColor',col,'MarkerFaceColor',col,'Marker','.'); + % hold on + % scatter(cf(2:end),worst_fec_cross(2:end),15,'DisplayName',['Worst'],'MarkerEdgeColor',col,'MarkerFaceColor',col,'Marker','.','HandleVisibility','off'); + % plot(cf(2:end),avg_fec_cross(2:end),'DisplayName',['AVG'],'LineWidth',1,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2); + % plot(cf(2:end),worst_fec_cross(2:end),'DisplayName',['AVG'],'LineWidth',0.5,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2); + % ghzgrid = hz2nm(nm2hz(1310)+[0:1:20].*400e9); + % set(gca,'xtick',sort(ghzgrid)) + % xlim([min(cf(cf~=0)), 1310.1]); + % + % + % % With matlab internal errorbar function... + % figure(221) + % %plot(cf,avg_fec_cross,'DisplayName',['AVG'],'LineWidth',1,'Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2); + % hold on + % %plot(cf_tot,min(S_chann(1:length(cf_tot),:),[],2),'DisplayName',['AVG'],'LineWidth',1,'LineStyle',':','Color',col,'Marker','^','MarkerFaceColor',col,'MarkerSize',2); + % plot(cf,worst_fec_cross,'DisplayName',['AVG'],'LineWidth',2,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2); + % ghzgrid = hz2nm(nm2hz(1310)+[0:1:20].*400e9); + % set(gca,'xtick',sort(ghzgrid)) + % xlim([1302, 1310.1]); + % xline(ghzgrid,'LineStyle',':','Color',[.7 .7 .7]); + + + %with + % Stot = movmean(Stot,5); + % figure(222) + % [hl,hp] = boundedline(cf_tot,Stot,[(Stot'-min(S_chann(1:length(cf_tot),:),[],2)),(max(S_chann(1:length(cf_tot),:),[],2)-Stot')], 'alpha','Color',col,'transparency', 0.05); + % ho = outlinebounds(hl,hp); + % set(ho, 'linestyle', ':', 'color', col, 'marker', '.','linewidth',0.5); + % hold on + % + % ghzgrid = hz2nm(nm2hz(1310)+[0:1:12].*400e9); + % xline(ghzgrid); + + + + + %plot the total ber + + % %figure(22); + % hold on; + % b= movmean(Stot,3); + % plot(AxesMain,cf_tot,b,'LineWidth',2,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname],'Color',col,'Marker','o'); + % hold on + + + + + + %scatter(AxesMain,zdw_,S,'Marker','+','MarkerEdgeColor',col,'MarkerFaceAlpha',0.4,'MarkerEdgeAlpha',0.4,'LineWidth',0.5,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); + %ylim(AxesMain,[-9.3 -7]); + + % for i = 1:numel(chp) + % hold on + % xline(AxesMain,chp(i),'Color',colr(i,:),'DisplayName',['CH: ', num2str(i)],'LineWidth',1.5); + % hold off + % end + + + + + % + % a = movmean(sortrows([zdw_; S]'),10,'Endpoints','discard'); + % + % sorted = sortrows([zdw_; S]'); + % figure(2) + % scatter(sorted(:,1),sorted(:,2)) + % + % ber_sorted = sort(S); + % mean(ber_sorted); + % std(ber_sorted); + % z1 = []; + % penalty = mean(ber_sorted):0.01:mean(ber_sorted)+2; + % for i = 1:length(penalty) + % l = penalty(i); + % if i == 1 + % z1 = [z1 sum(ber_sorted(1,:)l-0.01) / length(ber_sorted) ]; + % end + % + % end + % + % penalty_higherthan = 0.5; + % probability = sum(z1(find(penalty>mean(ber_sorted)+penalty_higherthan))); + % disp(['A penalty of more than 1dB has a probability of: ', num2str(probability)]); + % % + % stem(AxesMain,penalty,z1,"filled",'Marker','o','MarkerSize',2,'Color',col); + % + % + % [f1,x1]=ecdf(S(end,:)); + % %figure(23);plot(AxesMain,x1,f1,'r','LineWidth',3, 'Color',col); + % + % %plot(AxesMain,a(:,1),a(:,2),'Color',col+1,'Parent', AxesMain(1)); + % + % % histogram(AxesMain,S,1000,'EdgeColor','none','FaceAlpha',0.4); + % + % + % + % % + % %scatter(AxesMain,zdw_,S,'Marker','+','MarkerEdgeColor',col,'MarkerFaceAlpha',0.4,'MarkerEdgeAlpha',0.4,'LineWidth',0.5,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); + % hold on + % %scatter(AxesMain,zdw_chann(:,1),mean(S_chann,2),'Marker','diamond','MarkerEdgeColor',col,'MarkerFaceAlpha',0.6,'LineWidth',7,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); + % hold off + % % + % % for rlz = 1:size(S,2) + % % zdwval = zdw_(rlz); + % % feccrossing = S(rlz); + % % scatter(AxesMain,zdwval,feccrossing,10,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); + % % end + % + % xline([1302.03471732576,1304.30061112288,1306.57440520904,1308.85614097425,1311.14586009814,1313.44360455251,1315.74941660391,1318.06333881618]); + + +end + +function vec = removeZeros(vec) + % Find rows that contain only zeros + rows_to_remove = all(vec == 0, 2); + + % Remove rows with only zeros + vec(rows_to_remove, :) = []; end \ No newline at end of file diff --git a/Classes/Warehouse_class/functions/fwm_plots/plotBerVsZdwFailureRate.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotBerVsZdwFailureRate.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/plotBerVsZdwFailureRate.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotBerVsZdwFailureRate.m index 480641f..069dbc8 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotBerVsZdwFailureRate.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotBerVsZdwFailureRate.m @@ -1,157 +1,157 @@ -function plotBerVsZdwFailureRate(wh,plotJob) - - fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); - - if isvalid(fig) - figure(fig) - fig = get(fig); - AxesMain = fig.CurrentAxes; - hold on - else - fig = figure('name',char(plotJob.figName)); - AxesMain = gca; - hold on - end - - - col = plotJob.color; - - % we want to fetch all realizations - realization = wh.parameter.realization.values(1:end); - - % get all xAxis values - xAxis = wh.parameter.p_out.values; - - % get all center wavelengths - wavelengths = wh.parameter.center_wavelength.values; - %wavelengths = wavelengths(2:end); -% totber = NaN(numel(realization),1,numel(xAxis),numel(wavelengths)); -% ber = zeros(numel(realization),plotJob.ch,numel(xAxis),numel(wavelengths)); - - %get BER values for query - for w = 1:numel(wavelengths) - - for xl = 1:numel(xAxis) - - c_wavelen = wavelengths(w); - p_out = xAxis(xl); - - % dim1 : realiz; dim2: channels, dim3: rop, dim4, c_wavelength - temp = removeZeros(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,c_wavelen,plotJob.channelspacing,plotJob.randzdw)); - ber(1:size(temp,1),:,xl,w) = temp; - - end - end - - hdfec = 3.8e-3.*ones(size(xAxis)); - zdw_ = []; - zdw_chann = []; - zdw_tot = []; - cf_tot = []; - cf_ = []; - S = []; - Stot = []; - S_chann = []; - cf_chann = []; - - cnt = 0; - %get fec thresholds - %linear = squeeze(linear); - for c_wavelen = 1:size(ber,4) - for realiz = 1:size(ber,1) - for chann = 1:size(ber,2) - - %finde Schnittpunkt zwischen FEC und BER Kurve - temp_ber = squeeze(ber(realiz,chann,:,c_wavelen)).'; - if ~all(temp_ber == 0) - %nur wenn nicht alles nullen sind - crossing_ch = InterX([hdfec;xAxis],[temp_ber;xAxis]); - else - continue - end - - %Req. FEC Ergebnis einsortieren - if ~isempty(crossing_ch) - if crossing_ch(2) == 0 - print("d") - end - S(realiz,chann,c_wavelen) = crossing_ch(2); - else - S(realiz,chann,c_wavelen) = -1; - cnt = cnt +1; - end - - - end - end - end - - temp_max = -inf; - sum_FEC_not_crossed=[]; - sum_FEC_crossed=[]; - - threshold = plotJob.p_in - 10; - - for i = 1:plotJob.ch - hold on - %S:: 1.dim: realiz; 2.dim: channel; 3.dim: center wavelength - %squeeze a channel: - temp_data = squeeze(S(:,i,:)); - - %remove realizations that have no entry (only zero) - temp_data = removeZeros(temp_data); - - %replace zeros with NAN (e.g. for the wavelengths that have missing realizations) - temp_data(temp_data==0) = NaN; - - %for current channel - FEC_crossed = temp_data < threshold & ~isnan(temp_data); - FEC_not_crossed = temp_data >= threshold & ~isnan(temp_data); - - %sum over channels for overall picture - sum_FEC_not_crossed(i,:) = sum(FEC_not_crossed); - sum_FEC_crossed(i,:) = sum(FEC_crossed); - - failure_rate_channelwise(i,:) = sum_FEC_not_crossed(i,:)./ ( sum_FEC_crossed(i,:) + sum_FEC_not_crossed(i,:)); - - end - - failure_rate_total = sum(sum_FEC_not_crossed,1) ./ ( sum(sum_FEC_crossed,1) + sum(sum_FEC_not_crossed,1) ); - % plot failure rate (nbetween 0 and 1) - - plot(wavelengths,failure_rate_total,'Color',plotJob.color,'LineWidth',1,'LineStyle',plotJob.plotTypeArg,'Marker','x','MarkerSize',5,'MarkerFaceColor',plotJob.color,'DisplayName',plotJob.displayname); - - %plot max overall - %scatter(wavelengths,failure_rate_channelwise ,35,plotJob.color,'Marker','.'); - - ylabel('Failure Rate of Link'); - xlabel('Wavelength in nm'); - - xlim([min(wavelengths),max(wavelengths) ]); - ylim([0,1]); - - grid minor; - set(gca, 'color', 'none'); - legend = []; - -% fontsize(AxesMain,8,"points") - - fig.Position = plotJob.Position; - fig.Units = "centimeters"; - fig.Position = [0 0 12 5 7]; - - try - set(AxesMain,'TickLabelInterpreter','latex') - - set(AxesMain.Legend,'Interpreter','latex') - end - -end - -function vec = removeZeros(vec) - % Find rows that contain only zeros - rows_to_remove = all(vec == 0, 2); - - % Remove rows with only zeros - vec(rows_to_remove, :) = []; +function plotBerVsZdwFailureRate(wh,plotJob) + + fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); + + if isvalid(fig) + figure(fig) + fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on + else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on + end + + + col = plotJob.color; + + % we want to fetch all realizations + realization = wh.parameter.realization.values(1:end); + + % get all xAxis values + xAxis = wh.parameter.p_out.values; + + % get all center wavelengths + wavelengths = wh.parameter.center_wavelength.values; + %wavelengths = wavelengths(2:end); +% totber = NaN(numel(realization),1,numel(xAxis),numel(wavelengths)); +% ber = zeros(numel(realization),plotJob.ch,numel(xAxis),numel(wavelengths)); + + %get BER values for query + for w = 1:numel(wavelengths) + + for xl = 1:numel(xAxis) + + c_wavelen = wavelengths(w); + p_out = xAxis(xl); + + % dim1 : realiz; dim2: channels, dim3: rop, dim4, c_wavelength + temp = removeZeros(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,c_wavelen,plotJob.channelspacing,plotJob.randzdw)); + ber(1:size(temp,1),:,xl,w) = temp; + + end + end + + hdfec = 3.8e-3.*ones(size(xAxis)); + zdw_ = []; + zdw_chann = []; + zdw_tot = []; + cf_tot = []; + cf_ = []; + S = []; + Stot = []; + S_chann = []; + cf_chann = []; + + cnt = 0; + %get fec thresholds + %linear = squeeze(linear); + for c_wavelen = 1:size(ber,4) + for realiz = 1:size(ber,1) + for chann = 1:size(ber,2) + + %finde Schnittpunkt zwischen FEC und BER Kurve + temp_ber = squeeze(ber(realiz,chann,:,c_wavelen)).'; + if ~all(temp_ber == 0) + %nur wenn nicht alles nullen sind + crossing_ch = InterX([hdfec;xAxis],[temp_ber;xAxis]); + else + continue + end + + %Req. FEC Ergebnis einsortieren + if ~isempty(crossing_ch) + if crossing_ch(2) == 0 + print("d") + end + S(realiz,chann,c_wavelen) = crossing_ch(2); + else + S(realiz,chann,c_wavelen) = -1; + cnt = cnt +1; + end + + + end + end + end + + temp_max = -inf; + sum_FEC_not_crossed=[]; + sum_FEC_crossed=[]; + + threshold = plotJob.p_in - 10; + + for i = 1:plotJob.ch + hold on + %S:: 1.dim: realiz; 2.dim: channel; 3.dim: center wavelength + %squeeze a channel: + temp_data = squeeze(S(:,i,:)); + + %remove realizations that have no entry (only zero) + temp_data = removeZeros(temp_data); + + %replace zeros with NAN (e.g. for the wavelengths that have missing realizations) + temp_data(temp_data==0) = NaN; + + %for current channel + FEC_crossed = temp_data < threshold & ~isnan(temp_data); + FEC_not_crossed = temp_data >= threshold & ~isnan(temp_data); + + %sum over channels for overall picture + sum_FEC_not_crossed(i,:) = sum(FEC_not_crossed); + sum_FEC_crossed(i,:) = sum(FEC_crossed); + + failure_rate_channelwise(i,:) = sum_FEC_not_crossed(i,:)./ ( sum_FEC_crossed(i,:) + sum_FEC_not_crossed(i,:)); + + end + + failure_rate_total = sum(sum_FEC_not_crossed,1) ./ ( sum(sum_FEC_crossed,1) + sum(sum_FEC_not_crossed,1) ); + % plot failure rate (nbetween 0 and 1) + + plot(wavelengths,failure_rate_total,'Color',plotJob.color,'LineWidth',1,'LineStyle',plotJob.plotTypeArg,'Marker','x','MarkerSize',5,'MarkerFaceColor',plotJob.color,'DisplayName',plotJob.displayname); + + %plot max overall + %scatter(wavelengths,failure_rate_channelwise ,35,plotJob.color,'Marker','.'); + + ylabel('Failure Rate of Link'); + xlabel('Wavelength in nm'); + + xlim([min(wavelengths),max(wavelengths) ]); + ylim([0,1]); + + grid minor; + set(gca, 'color', 'none'); + legend = []; + +% fontsize(AxesMain,8,"points") + + fig.Position = plotJob.Position; + fig.Units = "centimeters"; + fig.Position = [0 0 12 5 7]; + + try + set(AxesMain,'TickLabelInterpreter','latex') + + set(AxesMain.Legend,'Interpreter','latex') + end + +end + +function vec = removeZeros(vec) + % Find rows that contain only zeros + rows_to_remove = all(vec == 0, 2); + + % Remove rows with only zeros + vec(rows_to_remove, :) = []; end \ No newline at end of file diff --git a/Classes/Warehouse_class/functions/fwm_plots/plotChannelSpacingAna.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotChannelSpacingAna.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/plotChannelSpacingAna.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotChannelSpacingAna.m index 44febc6..b64a827 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotChannelSpacingAna.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotChannelSpacingAna.m @@ -1,51 +1,51 @@ -function plotChannelSpacingAna(wh,plotJob) - -xAxis = wh.parameter.p_out.values; - - -realization = wh.parameter.realization.values(1:end); - -channelsp = wh.parameter.channelspacing.values(1:end); -channelsp = [200 400].*1e9; -for ch = 1:2 - channspacing = channelsp(ch); - for xl = 1:numel(xAxis) - p_out = xAxis(xl); - - curber = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.numchannels,channspacing); - curzdw = wh.getStoValue('zdw',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.numchannels,channspacing); - - ber(1:size(curber,1),1:size(curber,2),xl) = curber; - zdw(1:size(curber,1),1,xl) = curzdw; - - end - - ber = squeeze(mean(ber,1)); - zdw = squeeze(mean(zdw,1)); - - hdfec = 3.8e-3.*ones(size(xAxis)); - S = []; - wavelength={}; - - zdw_ = []; - zdw_chann = []; - zdw_tot = []; - S = []; - S_chann = []; - wl = round([1302.03471732576,1304.30061112288,1306.57440520904,1308.85614097425,1311.14586009814,1313.44360455251,1315.74941660391,1318.06333881618],2); - wl = 1:16; - wl = [1.2930 1.2953 1.2975 1.2998 1.3020 1.3043 1.3066 1.3089 1.3111 1.3134 1.3157 1.3181 1.3204 1.3227 1.3251 1.3274]; - - - a = InterX([hdfec(:)';xAxis],[mean(ber,1);xAxis]); - if ~isempty(a) - s(ch) = a(2); - else - s(ch) = NaN; - end -end - -figure(2224) -hold on -plot(channelsp,s,'LineWidth',1,'Color',plotJob.color,'Marker','o'); - +function plotChannelSpacingAna(wh,plotJob) + +xAxis = wh.parameter.p_out.values; + + +realization = wh.parameter.realization.values(1:end); + +channelsp = wh.parameter.channelspacing.values(1:end); +channelsp = [200 400].*1e9; +for ch = 1:2 + channspacing = channelsp(ch); + for xl = 1:numel(xAxis) + p_out = xAxis(xl); + + curber = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.numchannels,channspacing); + curzdw = wh.getStoValue('zdw',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.numchannels,channspacing); + + ber(1:size(curber,1),1:size(curber,2),xl) = curber; + zdw(1:size(curber,1),1,xl) = curzdw; + + end + + ber = squeeze(mean(ber,1)); + zdw = squeeze(mean(zdw,1)); + + hdfec = 3.8e-3.*ones(size(xAxis)); + S = []; + wavelength={}; + + zdw_ = []; + zdw_chann = []; + zdw_tot = []; + S = []; + S_chann = []; + wl = round([1302.03471732576,1304.30061112288,1306.57440520904,1308.85614097425,1311.14586009814,1313.44360455251,1315.74941660391,1318.06333881618],2); + wl = 1:16; + wl = [1.2930 1.2953 1.2975 1.2998 1.3020 1.3043 1.3066 1.3089 1.3111 1.3134 1.3157 1.3181 1.3204 1.3227 1.3251 1.3274]; + + + a = InterX([hdfec(:)';xAxis],[mean(ber,1);xAxis]); + if ~isempty(a) + s(ch) = a(2); + else + s(ch) = NaN; + end +end + +figure(2224) +hold on +plot(channelsp,s,'LineWidth',1,'Color',plotJob.color,'Marker','o'); + diff --git a/Classes/Warehouse_class/functions/fwm_plots/plotCurve.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotCurve.m similarity index 86% rename from Classes/Warehouse_class/functions/fwm_plots/plotCurve.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotCurve.m index a7134c9..be3813e 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotCurve.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotCurve.m @@ -1,278 +1,294 @@ -function plotCurve(wh,plotJob) -%PLOTCURVE Summary of this function goes here -% Detailed explanation goes here - -fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); - -if isvalid(fig) - figure(fig) - fig = get(fig); - AxesMain = fig.CurrentAxes; - hold on -else - fig = figure('name',char(plotJob.figName)); - AxesMain = gca; - hold on -end - -col = plotJob.color; - -% we want to fetch all realizations -if plotJob.pmd == 0 - realization = 499; - % realization = 0:7; -else - realization = wh.parameter.realization.values(1:end); -end -realization = wh.parameter.realization.values(1:end); -% get all xAxis values -xAxis = wh.parameter.p_out.values; - -% Fetch Data from Warehouse -for xl = 1:numel(xAxis) - p_out = xAxis(xl); - if string(plotJob.dataStatArg) == "Worst" - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - ber(xl) = max(temp,[],'all'); - linew = 1.0; - markersz = 3; - linestyle = '-'; - - elseif string(plotJob.dataStatArg) == "AVG" - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - ber(xl) = mean(temp,'all'); - linew = 1.0; - markersz = 3; - linestyle = '--'; - - elseif string(plotJob.dataStatArg) == "Best" - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - ber(xl) = min(temp,[],'all'); - linew = 1.0; - markersz = 3; - linestyle = '-'; - - elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)" - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - - ber(:,xl) = mean(temp,1,"omitnan").'; - - linew = 1; - markersz = 1; - linestyle = '-'; - - elseif string(plotJob.dataStatArg) == "Lineplot with quartiles" - - temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - temp = removeZeros(temp); - ber(xl) = mean(temp,"all","omitnan").'; - - - upperq(xl) = quantile(temp,0.99,"all"); - lowerq(xl) = quantile(temp,0.04,"all"); - - if lowerq(xl) == 0 - lowerq(xl) = lowerq(xl-1); - end - % upperq(xl) = 0.5*std(tmp,1,'all','omitnan'); - % lowerq(xl) = 0.5*std(tmp,1,'all','omitnan'); - - % upperq(xl) = max(dataNoNans); - % lowerq(xl) = min(dataNoNans); - - % upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp))); - % lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp))); - - linew = 1; - markersz = 1; - linestyle = '-'; - - elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations" - - raw_fetch = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).'; - tmp = reshape(raw_fetch,[],1); - ber(1:size(tmp,1),xl) = tmp; - - ber_per_chann(:,:,xl) = raw_fetch; - - linew = 0.3; - markersz = 2; - linestyle = ':'; - - end -end - - -%routine to remove total outliers (here those wehere the rop curve has a mean BER greater than 0.1) -% ber_per_chann_clean = NaN(size(ber_per_chann)); -% for ch = 1:size(ber_per_chann,1) -% bla = squeeze(ber_per_chann(ch,:,:)); -% ber_per_chann(ch,find(mean(bla,2)>0.25),:) = NaN; -% cleaned = rmoutliers(bla,"mean",'ThresholdFactor',2); -% -% ber_per_chann_clean(ch,1:size(cleaned,1),1:size(cleaned,2)) = cleaned; -% -% end -% -% ber = []; -% for rop = 1:size(ber_per_chann,3) -% temp = squeeze(ber_per_chann_clean(:,:,rop)); -% ber(rop) = mean(temp,"all","omitnan").'; -% upperq(rop) = quantile(temp,0.9,"all"); -% lowerq(rop) = quantile(temp,0.1,"all"); -% end - - - - -xAxis = xAxis; - -% Plot Data -if string(plotJob.plotTypeArg) == "Scatter" - for rlz = 1:size(ber,1) - - if rlz < size(ber,1) - scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'HandleVisibility','off'); - else - scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); - end - - end - -elseif string(plotJob.plotTypeArg) == "Lines" - - % if ~anynan(ber) - % [xAxis,ber] = interpCurve(xAxis, ber); - % end - - for rlz = 1:size(ber,1) -% - if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)") - ch = mod(rlz,plotJob.ch); - if ch == 0; ch = plotJob.ch; end - else - ch = plotJob.dataStatArg; - end - - if rlz < size(ber,1) - - - s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"none",'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off'); - - s.DataTipTemplate.Interpreter = "latex"; - s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; - s.DataTipTemplate.DataTipRows(1).Value = repmat(ch,size(ber)); - s.DataTipTemplate.DataTipRows(1); - s.DataTipTemplate.DataTipRows(2) = []; - - - else - - if string(plotJob.dataStatArg) == "Lineplot with quartiles" - [hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-o','alpha','Color',col,'transparency', 0.1,'linewidth',0.7); - hl.MarkerFaceColor = col; - hl.MarkerSize = 3; - set(hp,'HandleVisibility','off'); - %hp.LineWidth = 1.2; - - ho = outlinebounds(hl,hp); - set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.5); - set(ho,'HandleVisibility','off'); - else - - s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"o",'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]); - - s.DataTipTemplate.Interpreter = "latex"; - s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; - s.DataTipTemplate.DataTipRows(1).Value = repmat(string(ch),size(ber)); - s.DataTipTemplate.DataTipRows(1) - s.DataTipTemplate.DataTipRows(2) = []; - end - - end - - end -end - -% Draw FEC Threshold Line -%get x data of first children: -%get all linear Values -if 0 - linear = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,wh.parameter.p_out.values,0,0,499,"symmetric"); - linear = mean(linear,2); - lincurve = findall(AxesMain, 'Type', 'line','DisplayName','Linear Baseline'); - % - if isempty(lincurve) - xdata = AxesMain.Children(1).XData; - hdfec = 3.8e-3.*ones(size(xdata)); - plot(xdata,linear,'-','Marker',"o",'MarkerSize',3,'LineWidth',4,'Color',[0.6400 0.6400 0.6400],'MarkerFaceColor',[0.6400 0.6400 0.6400],'Parent', AxesMain,'DisplayName','Linear Baseline'); - %h = get(gca,'Children'); - %set(gca,'Children',[h(2) h(1)]) - end -end - -feccurve = findall(AxesMain, 'Type', 'line','DisplayName','FEC $3.8*10^{-3}$'); -% -if isempty(feccurve) - xdata = AxesMain.Children(1).XData; - hdfec = 3.8e-3.*ones(size(xdata)); - plot(xdata,hdfec,'--','MarkerSize',4,'Color','black','MarkerFaceColor','black','LineWidth',1,'Parent', AxesMain,'DisplayName','FEC $3.8*10^{-3}$','HandleVisibility','off'); - %h = get(gca,'Children'); - %set(gca,'Children',[h(2) h(1)]) -end - -% Figure Settings -%title(AxesMain,plotJob.title,"Interpreter","none"); - -xlabel(AxesMain,plotJob.xAxisLabel,"Interpreter","latex"); - -ylabel(AxesMain,plotJob.yAxisLabel,"Interpreter","latex"); - -set(AxesMain,'yscale','log'); - -grid(AxesMain,'on'); - -grid(AxesMain,'minor'); - -grid minor - -%legend(AxesMain); - -fontsize(AxesMain,8,"points") -% fontname(AxesMain,"Arial") - -fig.Position = plotJob.Position; -fig.Units = "centimeters"; -fig.Position = [2 2 8.5 7]; - -set(AxesMain,'TickLabelInterpreter','latex') - -set(AxesMain.Legend,'Interpreter','latex') -% set(gcf,'Units','centimeters') -% set(gcf,'Position',[2 2 9 4.5]) - -ylim([1e-5,0.3]); - -xlim([min(xAxis),-3]); - - -% annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points") - - -hold off - - -end - - -function vec = removeZeros(vec) - % Find rows that contain only zeros - rows_to_remove = all(vec == 0, 2); - - % Remove rows with only zeros - vec(rows_to_remove, :) = []; -end +function plotCurve(wh,plotJob) +%PLOTCURVE Summary of this function goes here +% Detailed explanation goes here + +fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); + +if isvalid(fig) + figure(fig) + % fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on +else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on +end + +col = plotJob.color; + +% we want to fetch all realizations +if plotJob.pmd == 0 + realization = 499; + % realization = 0:7; +else + realization = wh.parameter.realization.values(1:end); +end +realization = wh.parameter.realization.values(1:end); +% get all xAxis values +xAxis = wh.parameter.p_out.values; + +markerstyle = 'o'; +linestyle = '-'; + +% Fetch Data from Warehouse +for xl = 1:numel(xAxis) + p_out = xAxis(xl); + if string(plotJob.dataStatArg) == "Worst" + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + ber(xl) = quantile(temp,0.9,"all"); + % ber(xl) = max(temp,[],'all'); + linew = 1.0; + markersz = plotJob.markersize; + markerstyle = plotJob.markerstyle; + linestyle = plotJob.linestyle; + + elseif string(plotJob.dataStatArg) == "AVG" + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + ber(xl) = mean(temp,'all'); + linew = 1.0; + markersz = plotJob.markersize; + markerstyle = plotJob.markerstyle; + linestyle = plotJob.linestyle; + + elseif string(plotJob.dataStatArg) == "Best" + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + ber(xl) = min(temp,[],'all'); + linew = 1.0; + markersz = plotJob.markersize; + markerstyle = plotJob.markerstyle; + linestyle = plotJob.linestyle; + + elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)" + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + + ber(:,xl) = mean(temp,1,"omitnan").'; + + linew = 1; + markersz = 1; + markerstyle = plotJob.markerstyle; + linestyle = plotJob.linestyle; + + elseif string(plotJob.dataStatArg) == "Lineplot with quartiles" + + temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + temp = removeZeros(temp); + ber(xl) = mean(temp,"all","omitnan").'; + + + upperq(xl) = quantile(temp,0.99,"all"); + lowerq(xl) = quantile(temp,0.04,"all"); + + + % upperq(xl) = 0.5*std(tmp,1,'all','omitnan'); + % lowerq(xl) = 0.5*std(tmp,1,'all','omitnan'); + + upperq(xl) = max(temp(:)); + lowerq(xl) = min(temp(:)); + + if lowerq(xl) == 0 + lowerq(xl) = 1e-8; + end + + % upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp))); + % lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp))); + + linew = 1; + markersz = 1; + linestyle = '-'; + + elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations" + + raw_fetch = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).'; + tmp = reshape(raw_fetch,[],1); + ber(1:size(tmp,1),xl) = tmp; + + ber_per_chann(:,:,xl) = raw_fetch; + + linew = 0.3; + markersz = 2; + linestyle = ':'; + + end +end + + +%routine to remove total outliers (here those wehere the rop curve has a mean BER greater than 0.1) +% ber_per_chann_clean = NaN(size(ber_per_chann)); +% for ch = 1:size(ber_per_chann,1) +% bla = squeeze(ber_per_chann(ch,:,:)); +% ber_per_chann(ch,find(mean(bla,2)>0.25),:) = NaN; +% cleaned = rmoutliers(bla,"mean",'ThresholdFactor',2); +% +% ber_per_chann_clean(ch,1:size(cleaned,1),1:size(cleaned,2)) = cleaned; +% +% end +% +% ber = []; +% for rop = 1:size(ber_per_chann,3) +% temp = squeeze(ber_per_chann_clean(:,:,rop)); +% ber(rop) = mean(temp,"all","omitnan").'; +% upperq(rop) = quantile(temp,0.9,"all"); +% lowerq(rop) = quantile(temp,0.1,"all"); +% end + + + + +xAxis = xAxis; + +% Plot Data +if string(plotJob.plotTypeArg) == "Scatter" + for rlz = 1:size(ber,1) + + if rlz < size(ber,1) + scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'HandleVisibility','off'); + else + scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); + end + + end + +elseif string(plotJob.plotTypeArg) == "Lines" + + % if ~anynan(ber) + % [xAxis,ber] = interpCurve(xAxis, ber); + % end + + cols = cbrewer2('RdBu',size(ber,1)); + for rlz = 1:size(ber,1) +% + if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)") + ch = mod(rlz,plotJob.ch); + if ch == 0; ch = plotJob.ch; end + else + ch = plotJob.dataStatArg; + end + + if rlz < size(ber,1) + + col = cols(rlz,:); + + s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"none",'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off'); + + s.DataTipTemplate.Interpreter = "latex"; + s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; + s.DataTipTemplate.DataTipRows(1).Value = repmat(ch,size(ber)); + s.DataTipTemplate.DataTipRows(1); + s.DataTipTemplate.DataTipRows(2) = []; + + else + + if string(plotJob.dataStatArg) == "Lineplot with quartiles" + + [hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-o','alpha','Color',col,'transparency', 0.06,'linewidth',0.7); + hl.MarkerFaceColor = col; + hl.MarkerSize = 2; + set(hp,'HandleVisibility','off'); + %hp.LineWidth = 1.2; + + ho = outlinebounds(hl,hp); + set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.6); + set(ho,'HandleVisibility','off'); + + % errorbar(xAxis,ber(rlz,:),ber(rlz,:)-lowerq(rlz,:),upperq(rlz,:)-ber(rlz,:),'-o','Color',col,'linewidth',0.7); + + else + + s = plot(xAxis,ber(rlz,:),linestyle,'Marker',markerstyle,'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]); + + s.DataTipTemplate.Interpreter = "latex"; + s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; + s.DataTipTemplate.DataTipRows(1).Value = repmat(string(ch),size(ber)); + s.DataTipTemplate.DataTipRows(1) + s.DataTipTemplate.DataTipRows(2) = []; + end + + end + + end +end + +% Draw FEC Threshold Line +%get x data of first children: +%get all linear Values +if 0 + linear = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,wh.parameter.p_out.values,0,0,499,"symmetric"); + linear = mean(linear,2); + lincurve = findall(AxesMain, 'Type', 'line','DisplayName','Linear Baseline'); + % + if isempty(lincurve) + xdata = AxesMain.Children(1).XData; + hdfec = 3.8e-3.*ones(size(xdata)); + plot(xdata,linear,'-','Marker',"o",'MarkerSize',3,'LineWidth',4,'Color',[0.6400 0.6400 0.6400],'MarkerFaceColor',[0.6400 0.6400 0.6400],'Parent', AxesMain,'DisplayName','Linear Baseline'); + %h = get(gca,'Children'); + %set(gca,'Children',[h(2) h(1)]) + end +end + +feccurve = findall(AxesMain, 'Type', 'line','DisplayName','FEC $3.8*10^{-3}$'); +% +if isempty(feccurve) + xdata = AxesMain.Children(1).XData; + hdfec = 3.8e-3.*ones(size(xdata)); + plot(xdata,hdfec,'--','MarkerSize',4,'Color','black','MarkerFaceColor','black','LineWidth',1,'Parent', AxesMain,'DisplayName','FEC $3.8*10^{-3}$','HandleVisibility','off'); + %h = get(gca,'Children'); + %set(gca,'Children',[h(2) h(1)]) +end + +% Figure Settings +%title(AxesMain,plotJob.title,"Interpreter","none"); + +xlabel(AxesMain,plotJob.xAxisLabel,"Interpreter","latex"); + +ylabel(AxesMain,plotJob.yAxisLabel,"Interpreter","latex"); + +set(AxesMain,'yscale','log'); + +grid(AxesMain,'on'); + +grid(AxesMain,'minor'); + +grid minor + +%legend(AxesMain); + +fontsize(AxesMain,8,"points") +% fontname(AxesMain,"Arial") + + +% fig.Position = plotJob.Position; +% fig.Units = "centimeters"; +% fig.Position = [2 2 8.5 7]; + +set(AxesMain,'TickLabelInterpreter','latex') + +set(AxesMain.Legend,'Interpreter','latex') +% set(gcf,'Units','centimeters') +% set(gcf,'Position',[2 2 9 4.5]) + +ylim([1e-5,0.3]); + +xlim([-10,-4]); + + +% annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points") + + +hold off + + +end + + +function vec = removeZeros(vec) + % Find rows that contain only zeros + rows_to_remove = all(vec == 0, 2); + + % Remove rows with only zeros + vec(rows_to_remove, :) = []; +end diff --git a/Classes/Warehouse_class/functions/fwm_plots/plotHistogram.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotHistogram.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/plotHistogram.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotHistogram.m index 023a3a1..408cded 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotHistogram.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotHistogram.m @@ -1,151 +1,151 @@ -function plotHistogram(wh,plotJob) - - -fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); - -if isvalid(fig) - figure(fig) - fig = get(fig); - AxesMain = fig.CurrentAxes; - hold on -else - fig = figure('name',char(plotJob.figName)); - AxesMain = gca; - hold on -end - -col = plotJob.color; - -% we want to fetch all realizations -realization = wh.parameter.realization.values(1:end); - -% get all xAxis values -xAxis = wh.parameter.p_out.values; - - -% Fetch Data from Warehouse -for xl = 1:numel(xAxis) - p_out = xAxis(xl); - if string(plotJob.dataStatArg) == "Worst" - ber(xl) = max(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),[],'all'); - linew = 2.0; - markersz = 3; - linestyle = '-'; - elseif string(plotJob.dataStatArg) == "AVG" - ber(xl) = mean(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),'all'); - linew = 2.0; - markersz = 3; - linestyle = ':'; - elseif string(plotJob.dataStatArg) == "Best" - ber(xl) = min(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),[],'all'); - linew = 2.0; - markersz = 3; - linestyle = ':'; - - elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)" - tmp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)); - - if numel(tmp(tmp==0)) ~= 0 - disp('Removed all zero values!'); - tmp(tmp==0) = NaN; - end - ber(:,xl) = mean(tmp,1,"omitnan").'; - - linew = 0.7; - markersz = 2; - linestyle = '-'; - - elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations" - - - - tmp = reshape(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)).',[],1); - ber(1:size(tmp,1),xl) = tmp; - - linew = 0.3; - markersz = 2; - linestyle = ':'; - end -end - -disp('Removed all zero values!'); -ber(ber==0) = NaN; -if ~anynan(ber) - [xAxis,ber] = interpCurve(xAxis, ber); -end - -% plot FEC Crossing as histogram - -hdfec = 3.8e-3.*ones(size(xAxis)); -S = []; -for i = 1:size(ber,1) - a = InterX([hdfec;xAxis],[ber(i,:);xAxis]); - if ~isempty(a) - S(:,i) = a; - end -end - - -%% SUB 1 -AxesMain = subplot(2,1,1); - -hold on - -if ~isempty(S) - histogram(S(end,:),300,'Normalization','probability','FaceColor',col,'EdgeColor',col,'Parent',AxesMain,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname],'FaceAlpha',0.4,'EdgeAlpha',0.4); -end - -xlim([-3 ,9 ]); -ylim([0 .10]); - -% Figure Settings -title(AxesMain,['$P_{in}:$ ',num2str(plotJob.p_in-9), 'dBm; L : ', num2str(plotJob.l),'Km'],'Interpreter','latex'); - -xlabel(AxesMain,plotJob.xAxisLabel,'Interpreter','latex'); - -ylabel('PDF') - -grid(AxesMain,'on'); - -grid(AxesMain,'minor'); - -%legend(AxesMain,'Interpreter','latex'); - -fontsize(AxesMain,24,"pixels") - -hold off - - -%% SUB 2 -AxesMain = subplot(2,1,2); - -if ~isempty(S) -hold on - -[f1,x1]=ecdf(S(end,:)); - plot(x1,f1,'r','LineWidth',3, 'Color',col,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); -end -xlim([-3 ,9 ]); -ylim([0 1]); -% Figure Settings -title(AxesMain,['$P_{in}:$ ',num2str(plotJob.p_in-9), 'dBm; L : ', num2str(plotJob.l),'Km'],'Interpreter','latex'); - -xlabel(AxesMain,plotJob.xAxisLabel,'Interpreter','latex'); - -ylabel('CDF') - -grid(AxesMain,'on'); - -grid(AxesMain,'minor'); - -fontsize(AxesMain,24,"pixels") - -%legend(AxesMain,'Interpreter','latex'); - -fig.Position = plotJob.Position; - -hold off - - - +function plotHistogram(wh,plotJob) + + +fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); + +if isvalid(fig) + figure(fig) + fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on +else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on +end + +col = plotJob.color; + +% we want to fetch all realizations +realization = wh.parameter.realization.values(1:end); + +% get all xAxis values +xAxis = wh.parameter.p_out.values; + + +% Fetch Data from Warehouse +for xl = 1:numel(xAxis) + p_out = xAxis(xl); + if string(plotJob.dataStatArg) == "Worst" + ber(xl) = max(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),[],'all'); + linew = 2.0; + markersz = 3; + linestyle = '-'; + elseif string(plotJob.dataStatArg) == "AVG" + ber(xl) = mean(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),'all'); + linew = 2.0; + markersz = 3; + linestyle = ':'; + elseif string(plotJob.dataStatArg) == "Best" + ber(xl) = min(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),[],'all'); + linew = 2.0; + markersz = 3; + linestyle = ':'; + + elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)" + tmp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)); + + if numel(tmp(tmp==0)) ~= 0 + disp('Removed all zero values!'); + tmp(tmp==0) = NaN; + end + ber(:,xl) = mean(tmp,1,"omitnan").'; + + linew = 0.7; + markersz = 2; + linestyle = '-'; + + elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations" + + + + tmp = reshape(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)).',[],1); + ber(1:size(tmp,1),xl) = tmp; + + linew = 0.3; + markersz = 2; + linestyle = ':'; + end +end + +disp('Removed all zero values!'); +ber(ber==0) = NaN; +if ~anynan(ber) + [xAxis,ber] = interpCurve(xAxis, ber); +end + +% plot FEC Crossing as histogram + +hdfec = 3.8e-3.*ones(size(xAxis)); +S = []; +for i = 1:size(ber,1) + a = InterX([hdfec;xAxis],[ber(i,:);xAxis]); + if ~isempty(a) + S(:,i) = a; + end +end + + +%% SUB 1 +AxesMain = subplot(2,1,1); + +hold on + +if ~isempty(S) + histogram(S(end,:),300,'Normalization','probability','FaceColor',col,'EdgeColor',col,'Parent',AxesMain,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname],'FaceAlpha',0.4,'EdgeAlpha',0.4); +end + +xlim([-3 ,9 ]); +ylim([0 .10]); + +% Figure Settings +title(AxesMain,['$P_{in}:$ ',num2str(plotJob.p_in-9), 'dBm; L : ', num2str(plotJob.l),'Km'],'Interpreter','latex'); + +xlabel(AxesMain,plotJob.xAxisLabel,'Interpreter','latex'); + +ylabel('PDF') + +grid(AxesMain,'on'); + +grid(AxesMain,'minor'); + +%legend(AxesMain,'Interpreter','latex'); + +fontsize(AxesMain,24,"pixels") + +hold off + + +%% SUB 2 +AxesMain = subplot(2,1,2); + +if ~isempty(S) +hold on + +[f1,x1]=ecdf(S(end,:)); + plot(x1,f1,'r','LineWidth',3, 'Color',col,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]); +end +xlim([-3 ,9 ]); +ylim([0 1]); +% Figure Settings +title(AxesMain,['$P_{in}:$ ',num2str(plotJob.p_in-9), 'dBm; L : ', num2str(plotJob.l),'Km'],'Interpreter','latex'); + +xlabel(AxesMain,plotJob.xAxisLabel,'Interpreter','latex'); + +ylabel('CDF') + +grid(AxesMain,'on'); + +grid(AxesMain,'minor'); + +fontsize(AxesMain,24,"pixels") + +%legend(AxesMain,'Interpreter','latex'); + +fig.Position = plotJob.Position; + +hold off + + + end \ No newline at end of file diff --git a/Classes/Warehouse_class/functions/fwm_plots/plotViolin.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotViolin.m similarity index 95% rename from Classes/Warehouse_class/functions/fwm_plots/plotViolin.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotViolin.m index 2453744..ebe24ad 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotViolin.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotViolin.m @@ -1,206 +1,206 @@ -function plotViolin(wh,plotJob) - -fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); - -if isvalid(fig) - figure(fig) - fig = get(fig); - AxesMain = fig.CurrentAxes; - hold on -else - fig = figure('name',char(plotJob.figName)); - AxesMain = gca; - hold on -end - -%% Violin -col = plotJob.color; - -% we want to fetch all realizations -if plotJob.pmd == 0 - realization = 1; -else - realization = wh.parameter.realization.values(1:end); -end - -%realization = 0:8; - -% get all xAxis values -xAxis = wh.parameter.p_out.values; - -% ber = NaN(500,16,10); -% zdw = NaN(500,1,10); - -%get BER values for query -for xl = 1:numel(xAxis) - p_out = xAxis(xl); - - curber = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); - curber= removeZeros(curber); - ber(1:size(curber,1),1:size(curber,2),xl) = curber; - -end - -% to remove outliers set the percentile range -% a = squeeze(mean(ber,2)); -% out = isoutlier(mean(a,2),"percentiles",[0 100]); -% ber = ber(~out,:,:); -% disp(sum(out)); - -hdfec = 3.8e-3.*ones(size(xAxis)); -S = []; -wavelength={}; - - -S = []; -S_chann = []; - -wl = calcWavelengthPlan(plotJob.ch,plotJob.channelspacing,1310); -%get fec thresholds -% [C,ia,ib] =intersect(linx,xAxis); -% linear = squeeze(linear); - -%ber(ber==0) = NaN; -S_chann_no_crossing = zeros(1,plotJob.ch); -for chann = 1:size(ber,2) - - for realiz = 1:size(ber,1) - - ber_series = squeeze(ber(realiz,chann,:)).'; - if mean(ber_series) > 0.1 - continue - end - - a = InterX([hdfec(:)';xAxis],[ber_series;xAxis]); - - if ~isempty(a) - S_chann(realiz,chann) = a(2); -% if a(2) > -7 && string(plotJob.pol) == "copolarized" -% continue -% end - S(end+1) = a(2); - wavelength{end+1} = num2str(wl(chann)); - else - S(end+1) = 0; - wavelength{end+1} = num2str(wl(chann)); - S_chann_no_crossing(realiz,chann) = 1; - S_chann(realiz,chann) = -1; - end - - end -end - -threshold = -6; -FEC_crossed = sum(S_chann < threshold & ~isnan(S_chann),1); -FEC_not_crossed = sum(S_chann >= threshold & ~isnan(S_chann),1); -failure_rate = FEC_not_crossed ./ (FEC_crossed + FEC_not_crossed) ; - - -S_chann(S_chann==0) = NaN; - -total_avg = mean(S_chann,"all","omitnan"); - -%figure(2024) -%C = flip(cbrewer2('Spectral',8)); -if numel(S) <= numel(wl) - vs = scatter(1:numel(S),S,50,'o','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',1,'HandleVisibility','off'); - %vs = scatter(1,mean(S),50,'o','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',1); - -else - - vs = violinplot(S,wavelength,... - 'ViolinColor',plotJob.color,... - 'ViolinAlpha',0.1,... - 'MarkerSize',1,... - 'ShowMedian',false,... - 'EdgeColor',plotJob.color,... - 'ShowWhiskers',false,... - 'ShowData',false,... - 'ShowBox',false,... - 'Bandwidth',0.051 ... - ); - - - hold on - - partly_failed = boolean(ceil(failure_rate)); - - avg = mean(S_chann,1,"omitnan"); - - notfailed = ~partly_failed .* avg; - notfailed(notfailed==0) = NaN; - scatter(1:size(S_chann,2),notfailed,10,'Marker','x','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',0.5,'HandleVisibility','off'); - - - hold on - partly_failed = partly_failed.*avg; - partly_failed(partly_failed==0) = NaN; - s=scatter(1:numel(failure_rate),partly_failed,10,'Marker','x','LineWidth',0.5,'HandleVisibility','off','MarkerEdgeColor','red'); - s.DataTipTemplate.Interpreter = "latex"; - s.DataTipTemplate.DataTipRows(1).Label = "Fail Rate: "; - s.DataTipTemplate.DataTipRows(1).Value = failure_rate; - s.DataTipTemplate.DataTipRows(2) = []; - hold off -end - -% ax = gca; -% ax.XTicks - -hold on -yline(total_avg,'LineWidth',1,'LineStyle','--','DisplayName','System Avg.') -fig.Position = plotJob.Position; - -xticklabels(1:16); -ylabel('Penalty in dB'); -xlabel('Channel Number'); -ylim([-9.3,-3]); -xlim([0,plotJob.ch+1]); -grid minor; -set(gca, 'color', 'none'); -legend = []; - -fontsize(AxesMain,8,"points") - -fig.Position = plotJob.Position; -fig.Units = "centimeters"; -fig.Position = [2 2 8.5 7]; - -set(AxesMain,'TickLabelInterpreter','latex') - -set(AxesMain.Legend,'Interpreter','latex') - - - -if 0 -ber_sorted = sort(S); - -z1 = []; -penalty = mean(ber_sorted):0.01:mean(ber_sorted)+2; - -for i = 1:length(penalty) - l = penalty(i); - if i == 1 - z1 = [z1 sum(ber_sorted(1,:)l-0.01) / length(ber_sorted) ]; - end - -end - -penalty_higherthan = 0.5; -probability = sum(z1(find(penalty>mean(ber_sorted)+penalty_higherthan))); -disp(['A penalty of more than 0.5 dB has a probability of: ', num2str(probability)]); -end - -end - - -function vec = removeZeros(vec) - % Find rows that contain only zeros - rows_to_remove = all(vec == 0, 2); - - % Remove rows with only zeros - vec(rows_to_remove, :) = []; - - +function plotViolin(wh,plotJob) + +fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); + +if isvalid(fig) + figure(fig) + fig = get(fig); + AxesMain = fig.CurrentAxes; + hold on +else + fig = figure('name',char(plotJob.figName)); + AxesMain = gca; + hold on +end + +%% Violin +col = plotJob.color; + +% we want to fetch all realizations +if plotJob.pmd == 0 + realization = 1; +else + realization = wh.parameter.realization.values(1:end); +end + +%realization = 0:8; + +% get all xAxis values +xAxis = wh.parameter.p_out.values; + +% ber = NaN(500,16,10); +% zdw = NaN(500,1,10); + +%get BER values for query +for xl = 1:numel(xAxis) + p_out = xAxis(xl); + + curber = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw); + curber= removeZeros(curber); + ber(1:size(curber,1),1:size(curber,2),xl) = curber; + +end + +% to remove outliers set the percentile range +% a = squeeze(mean(ber,2)); +% out = isoutlier(mean(a,2),"percentiles",[0 100]); +% ber = ber(~out,:,:); +% disp(sum(out)); + +hdfec = 3.8e-3.*ones(size(xAxis)); +S = []; +wavelength={}; + + +S = []; +S_chann = []; + +wl = calcWavelengthPlan(plotJob.ch,plotJob.channelspacing,1310); +%get fec thresholds +% [C,ia,ib] =intersect(linx,xAxis); +% linear = squeeze(linear); + +%ber(ber==0) = NaN; +S_chann_no_crossing = zeros(1,plotJob.ch); +for chann = 1:size(ber,2) + + for realiz = 1:size(ber,1) + + ber_series = squeeze(ber(realiz,chann,:)).'; + if mean(ber_series) > 0.1 + continue + end + + a = InterX([hdfec(:)';xAxis],[ber_series;xAxis]); + + if ~isempty(a) + S_chann(realiz,chann) = a(2); +% if a(2) > -7 && string(plotJob.pol) == "copolarized" +% continue +% end + S(end+1) = a(2); + wavelength{end+1} = num2str(wl(chann)); + else + S(end+1) = 0; + wavelength{end+1} = num2str(wl(chann)); + S_chann_no_crossing(realiz,chann) = 1; + S_chann(realiz,chann) = -1; + end + + end +end + +threshold = -6; +FEC_crossed = sum(S_chann < threshold & ~isnan(S_chann),1); +FEC_not_crossed = sum(S_chann >= threshold & ~isnan(S_chann),1); +failure_rate = FEC_not_crossed ./ (FEC_crossed + FEC_not_crossed) ; + + +S_chann(S_chann==0) = NaN; + +total_avg = mean(S_chann,"all","omitnan"); + +%figure(2024) +%C = flip(cbrewer2('Spectral',8)); +if numel(S) <= numel(wl) + vs = scatter(1:numel(S),S,50,'o','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',1,'HandleVisibility','off'); + %vs = scatter(1,mean(S),50,'o','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',1); + +else + + vs = violinplot(S,wavelength,... + 'ViolinColor',plotJob.color,... + 'ViolinAlpha',0.1,... + 'MarkerSize',1,... + 'ShowMedian',false,... + 'EdgeColor',plotJob.color,... + 'ShowWhiskers',false,... + 'ShowData',false,... + 'ShowBox',false,... + 'Bandwidth',0.051 ... + ); + + + hold on + + partly_failed = boolean(ceil(failure_rate)); + + avg = mean(S_chann,1,"omitnan"); + + notfailed = ~partly_failed .* avg; + notfailed(notfailed==0) = NaN; + scatter(1:size(S_chann,2),notfailed,10,'Marker','x','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',0.5,'HandleVisibility','off'); + + + hold on + partly_failed = partly_failed.*avg; + partly_failed(partly_failed==0) = NaN; + s=scatter(1:numel(failure_rate),partly_failed,10,'Marker','x','LineWidth',0.5,'HandleVisibility','off','MarkerEdgeColor','red'); + s.DataTipTemplate.Interpreter = "latex"; + s.DataTipTemplate.DataTipRows(1).Label = "Fail Rate: "; + s.DataTipTemplate.DataTipRows(1).Value = failure_rate; + s.DataTipTemplate.DataTipRows(2) = []; + hold off +end + +% ax = gca; +% ax.XTicks + +hold on +yline(total_avg,'LineWidth',1,'LineStyle','--','DisplayName','System Avg.') +fig.Position = plotJob.Position; + +xticklabels(1:16); +ylabel('Penalty in dB'); +xlabel('Channel Number'); +ylim([-9.3,-3]); +xlim([0,plotJob.ch+1]); +% grid minor; +set(gca, 'color', 'none'); +legend = []; + +fontsize(AxesMain,8,"points") + +fig.Position = plotJob.Position; +fig.Units = "centimeters"; +fig.Position = [2 2 8.5 7]; + +set(AxesMain,'TickLabelInterpreter','latex') + +set(AxesMain.Legend,'Interpreter','latex') + + + +if 0 +ber_sorted = sort(S); + +z1 = []; +penalty = mean(ber_sorted):0.01:mean(ber_sorted)+2; + +for i = 1:length(penalty) + l = penalty(i); + if i == 1 + z1 = [z1 sum(ber_sorted(1,:)l-0.01) / length(ber_sorted) ]; + end + +end + +penalty_higherthan = 0.5; +probability = sum(z1(find(penalty>mean(ber_sorted)+penalty_higherthan))); +disp(['A penalty of more than 0.5 dB has a probability of: ', num2str(probability)]); +end + +end + + +function vec = removeZeros(vec) + % Find rows that contain only zeros + rows_to_remove = all(vec == 0, 2); + + % Remove rows with only zeros + vec(rows_to_remove, :) = []; + + end \ No newline at end of file diff --git a/Classes/Warehouse_class/functions/fwm_plots/plot_ber_distribution.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plot_ber_distribution.m similarity index 96% rename from Classes/Warehouse_class/functions/fwm_plots/plot_ber_distribution.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plot_ber_distribution.m index 1d43674..8abf21b 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plot_ber_distribution.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plot_ber_distribution.m @@ -1,58 +1,58 @@ - -%automate plots -% [file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\session_januar24\wh_complete_at_1310.mat"); -% wh = load([path filesep file]); -% wh = wh.wh; - -plotJob = struct(); - -plotJob.l = 10; -plotJob.ch = 16; -plotJob.d = 3; -plotJob.sgm = 1; -plotJob.pol = "copolarized"; -plotJob.p_in = 3; -plotJob.gamma = 0.0023; -plotJob.pmd = 0.1; -plotJob.channelspacing = 400e9; -plotJob.randzdw = 0; - -ber_per_chann = []; -% get all xAxis values -xAxis = wh.parameter.p_out.values; -realization = wh.parameter.realization.values(1:end); -% Fetch Data from Warehouse -for xl = 1:numel(xAxis) - p_out = xAxis(xl); - raw_fetch = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).'; - - ber_per_chann(:,:,xl) = raw_fetch; -end -%% - -figure(2023); - -for ch = [1,floor(plotJob.ch/2),ceil(plotJob.ch/2)+1,plotJob.ch] %1:15:size(ber_per_chann,1) - - for p = 5%1:size(ber_per_chann,3) - - % Extract data for the current row - row_data = squeeze(ber_per_chann(ch,:,p)); - [f, xi] = ksdensity(row_data); - % Identify the peak point - [max_density, max_index] = max(f); - peak_x = xi(max_index); - - end - % Create a histogram plot for the current row with a unique color - plot(xi, f, 'LineWidth', 2, 'DisplayName', ['Ch. ', num2str(ch)],'LineStyle','--'); - %histogram(row_data,100, 'DisplayName', ['Channel ', num2str(ch)], 'EdgeColor', 'none'); - - hold on; % Hold the plot for the next iteration - text(peak_x, max_density, ['Ch ', num2str(ch)], 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'left'); -end - - -legend show - + +%automate plots +% [file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\session_januar24\wh_complete_at_1310.mat"); +% wh = load([path filesep file]); +% wh = wh.wh; + +plotJob = struct(); + +plotJob.l = 10; +plotJob.ch = 16; +plotJob.d = 3; +plotJob.sgm = 1; +plotJob.pol = "copolarized"; +plotJob.p_in = 3; +plotJob.gamma = 0.0023; +plotJob.pmd = 0.1; +plotJob.channelspacing = 400e9; +plotJob.randzdw = 0; + +ber_per_chann = []; +% get all xAxis values +xAxis = wh.parameter.p_out.values; +realization = wh.parameter.realization.values(1:end); +% Fetch Data from Warehouse +for xl = 1:numel(xAxis) + p_out = xAxis(xl); + raw_fetch = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).'; + + ber_per_chann(:,:,xl) = raw_fetch; +end +%% + +figure(2023); + +for ch = [1,floor(plotJob.ch/2),ceil(plotJob.ch/2)+1,plotJob.ch] %1:15:size(ber_per_chann,1) + + for p = 5%1:size(ber_per_chann,3) + + % Extract data for the current row + row_data = squeeze(ber_per_chann(ch,:,p)); + [f, xi] = ksdensity(row_data); + % Identify the peak point + [max_density, max_index] = max(f); + peak_x = xi(max_index); + + end + % Create a histogram plot for the current row with a unique color + plot(xi, f, 'LineWidth', 2, 'DisplayName', ['Ch. ', num2str(ch)],'LineStyle','--'); + %histogram(row_data,100, 'DisplayName', ['Channel ', num2str(ch)], 'EdgeColor', 'none'); + + hold on; % Hold the plot for the next iteration + text(peak_x, max_density, ['Ch ', num2str(ch)], 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'left'); +end + + +legend show + %% \ No newline at end of file diff --git a/Datatypes/clr.m b/Datatypes/clr.m index b9aa515..bda20d1 100644 --- a/Datatypes/clr.m +++ b/Datatypes/clr.m @@ -49,22 +49,5 @@ classdef clr hold off; end - function showSet(colorStruct) - % Show colors from a structure in a bar plot - names = fieldnames(colorStruct); - colors = cell2mat(struct2cell(colorStruct)'); - - figure; - hold on; - for i = 1:size(colors, 1) - fill([0 1 1 0], [i-1 i-1 i i], colors(i, :), 'EdgeColor', 'k'); - text(1.1, i-0.5, names{i}, 'FontSize', 12, 'Interpreter', 'none'); - end - ylim([0, size(colors, 1)]); - xlim([0, 1.5]); - axis off; - title('Color Preview'); - hold off; - end end end diff --git a/Datatypes/equalizer_structure.m b/Datatypes/equalizer_structure.m index c8250a6..b94e0c0 100644 --- a/Datatypes/equalizer_structure.m +++ b/Datatypes/equalizer_structure.m @@ -8,6 +8,7 @@ classdef equalizer_structure < int32 % db_precoded (3) vnle_db_mlse (4) db_encoded (5) + ml_mlse (6) end end \ No newline at end of file diff --git a/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m index 09e5e27..4d207bd 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -13,14 +13,13 @@ arguments end try - - % Initialize output structures output.ffe_package = {}; output.mlse_package = {}; output.vnle_package = {}; output.dbtgt_package = {}; output.dbenc_package = {}; + output.mlmlse_package = {}; if options.mode == "load_run_id" || options.append_to_db % Initialize database connection @@ -96,11 +95,12 @@ try adaption= 1; use_dd_mode = 1; - use_ffe = 1; - use_dfe = 1; - use_vnle_mlse = 1; - use_dbtgt = 1; + use_ffe = 0; + use_dfe = 0; + use_vnle_mlse = 0; + use_dbtgt = 0; use_dbenc = 0; + use_ml_mlse = 1; addProcessingResultToDatabase = 0; @@ -130,7 +130,7 @@ try mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0,"adaption_technique","lms"); - eq_post =FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode); + eq_post = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode); % Duobinary signaling (db encoded) mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels); eq_db_enc = EQ("Ne", ffe_order, "Nb", dfe_order, "training_length", len_tr, ... @@ -140,7 +140,12 @@ try % Preprocess signal Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym); - % Scpe_sig.spectrum("fignum",2223,"normalizeTo0dB",1,"displayname",'Rx'); + Scpe_sig.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',-6); + + Scpe_sig.spectrum("fignum",201,"normalizeTo0dB",0,"displayname",'Rx'); + + ylim([-30,3]); + xlim([-5,100]); % Scpe_sig.spectrum("fignum",22233,"normalizeTo0dB",0,"displayname",'Rx'); % Scpe_sig.eye(fsym,M,"fignum",1024); @@ -194,14 +199,26 @@ try pf_ncoeffs = 1; ffe_order = [50, 5, 5]; - eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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); + eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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_ = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0.0004,"order",[50,5,5],"sps",2,"decide",0); pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); useviterbi = 0; if useviterbi mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); else - mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + + if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation + trellexlusion = 1; + else + trellexlusion = 0; + end + + %state_mode 3 -> stat lvl; state_mode 2 -> use target lvls + %scale_mode 2 -> mmse adaption + + mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',2); + end [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... @@ -222,30 +239,25 @@ try database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config); end - % pf_ncoeffs = 2; - % eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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); - % pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); - % % mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - % mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - % - % [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... - % "precode_mode", duob_mode,... - % 'showAnalysis', 0, ... - % "postFFE", [],... - % "eth_style_symbol_mapping", 0); - % - % ffe_results.metrics.print; - % mlse_results.metrics.print; - % - % output.mlse_package{r} = mlse_results; - % output.vnle_package{r} = ffe_results; - % - % if options.append_to_db - % database.addProcessingResult(run_id, mlse_results.metrics, mlse_results.config); - % database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config); - % end + end + + if use_ml_mlse + + %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",length(Scpe_sig),"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ... + "traceback_depth",128,"L",1,"delta",4,"adaptive_mu",0); + + [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Scpe_sig, Symbols, Tx_bits,"precode_mode",duob_mode); + output.mlmlse_package{r} = ml_mlse_results; + + if options.append_to_db + database.addProcessingResult(run_id, ml_mlse_results.metrics, ml_mlse_results.config); + end end + if use_dbtgt @@ -253,7 +265,12 @@ try if useviterbi mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); else - mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); + if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation + trellexlusion = 1; + else + trellexlusion = 0; + end + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',3); end ffe_order = [50, 5, 5]; eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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); @@ -276,6 +293,9 @@ try if duob_mode == db_mode.db_encoded + mlse_db_enc = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels); + db_results = duobinary_signaling(eq_db_enc, mlse_db_enc, M, Scpe_sig, Symbols, Tx_bits, "precode_mode",duob_mode, "showAnalysis",0,"postFFE",[]); output.dbenc_package{r} = db_results; if options.append_to_db diff --git a/Functions/EQ_structures/duobinary_signaling.m b/Functions/EQ_structures/duobinary_signaling.m index e47bc19..7f13c35 100644 --- a/Functions/EQ_structures/duobinary_signaling.m +++ b/Functions/EQ_structures/duobinary_signaling.m @@ -35,9 +35,28 @@ if ~isempty(options.postFFE) [eq_signal, eq_noise] = options.postFFE.process(eq_signal, tx_symbols); end -% Process through MLSE -% [mlse_signal] = mlse_.process(eq_signal); -[mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); +if isa(mlse_,'MLSE_viterbi') + [mlse_signal] = mlse_.process(eq_signal); +else + + % Aufpassen mit welcher Sequenz man hier vergleicht für LLR stuff... + % gespeichtere "Symbols" sind schon DB codiert, das wollen wir hier + % nicht! Sondern die precoded aber nicht db-encoded müssen als ref in + % die LLR berechnung gehen! + ref_sym = PAMmapper(M,0).map(tx_bits); %ist klar + ref_sym_dpc = Duobinary().precode(ref_sym); % precoded + % ref_sym_dbenc = Duobinary().encode(ref_sym_dpc); %encoded - das wurde gesendet! + % ref_sym_dec = Duobinary().decode(ref_sym_dbenc); %ref_sym wieder zurück! + + mlse_.trellis_states = PAMmapper(M,0).levels; + mlse_.trellis_state_mode = 1; + [mlse_signal,LLR,GMI_MLSE] = mlse_.process(eq_signal,ref_sym_dpc); +end + + + +% tx_symbols_ = Duobinary().decode(tx_symbols); +% [mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); % Apply duobinary encoding and decoding mlse_signal = Duobinary().encode(mlse_signal); diff --git a/Functions/EQ_structures/duobinary_target.m b/Functions/EQ_structures/duobinary_target.m index 3a1389f..36150dd 100644 --- a/Functions/EQ_structures/duobinary_target.m +++ b/Functions/EQ_structures/duobinary_target.m @@ -130,6 +130,10 @@ db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse); db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)'; if options.showAnalysis + + eq_signal.eye(eq_signal.fs,M,"fignum",249); + + eq_noise = eq_noise - mean(eq_noise.signal); rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum"); diff --git a/Functions/EQ_structures/ml_mlse.m b/Functions/EQ_structures/ml_mlse.m new file mode 100644 index 0000000..f2d468d --- /dev/null +++ b/Functions/EQ_structures/ml_mlse.m @@ -0,0 +1,140 @@ +function [ml_mlse_results] = ml_mlse(eq_, M, rx_signal, tx_symbols, tx_bits, options) +% +% +% Inputs: +% eq_ - Equalizer object +% M - Modulation order +% rx_signal - Received signal +% tx_symbols - Transmitted symbols +% tx_bits - Transmitted bits +% options - Optional parameters +% +% Outputs: +% ffe_results - Results from FFE processing + +arguments + eq_ + M + rx_signal + tx_symbols + tx_bits + options.precode_mode db_mode + options.eth_style_symbol_mapping = 0; + options.postFFE = []; + +end + +%% Process signals through equalizer + +[eq_signal_hd,y_ref] = eq_.process(rx_signal,tx_symbols); + +%% Calculate BER based on precoding mode +[bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping); + + +% Create FFE results structure +ml_mlse_results = struct(); +try + eq_.e = []; + eq_.e2 = []; + eq_.e3 = []; + eq_.b = []; + eq_.b2 = []; + eq_.b3 = []; +end + +ml_mlse_results.config = Equalizerstruct(); + +eq_small = strip_eq(eq_, 10); +json_str = jsonencode(eq_small); + +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'; + +ml_mlse_results.metrics = Metricstruct; +% ml_mlse_results.metrics.result_id = NaN; +% ml_mlse_results.metrics.run_id = NaN; +% ml_mlse_results.metrics.eqParam_id = NaN; +ml_mlse_results.metrics.date_of_processing = datetime('now'); +ml_mlse_results.metrics.BER = ber; +ml_mlse_results.metrics.numBits = bits; +ml_mlse_results.metrics.numBitErr = errors; +ml_mlse_results.metrics.BER_precoded = ber_precoded; +ml_mlse_results.metrics.numBitErr_precoded = errors_precoded; +% ml_mlse_results.metrics.SNR = NaN; +% ml_mlse_results.metrics.SNR_level = NaN; +% ml_mlse_results.metrics.STD = NaN; +% ml_mlse_results.metrics.STD_level = NaN; +% ml_mlse_results.metrics.STDrx = NaN; +% ml_mlse_results.metrics.STDrx_level = NaN; +% ml_mlse_results.metrics.GMI = NaN; +% ml_mlse_results.metrics.AIR = NaN; +% ml_mlse_results.metrics.EVM = NaN; +% ml_mlse_results.metrics.EVM_level = NaN; +% ml_mlse_results.metrics.Alpha = NaN; + + +end + +%% Helper Functions +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); + +switch precode_mode + case db_mode.no_db + % TX Data is not precoded + % A) Emulate diff precoding + eq_signal_hd_precoded = Duobinary().encode(eq_signal_hd, "M", M); + eq_signal_hd_precoded = Duobinary().decode(eq_signal_hd_precoded, "M", M); + + tx_symbols_precoded = Duobinary().encode(tx_symbols); + tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); + + 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", 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", 30000, "skip_end", 150, "returnErrorLocation", 1); + + case db_mode.db_precoded + % Data is precoded on TX side + % A) Decode at Rx if no DB targeting was applied + 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", 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", 30000, "skip_end", 150, "returnErrorLocation", 1); +end +end + + +function eq_out = strip_eq(eq_, max_elems) + % strip_eq removes all large fields from the ML_MLSE object + % eq_out = strip_eq(eq_, max_elems) + % max_elems ... maximum number of elements to keep (default = 10) + + if nargin < 2 + max_elems = 10; % default threshold + end + + props = properties(eq_); + for i = 1:numel(props) + val = eq_.(props{i}); + if ~isempty(val) + % Count total number of elements + if numel(val) > max_elems + eq_.(props{i}) = []; + end + end + end + eq_out = eq_; +end diff --git a/Functions/EQ_structures/vnle_postfilter_mlse.m b/Functions/EQ_structures/vnle_postfilter_mlse.m index b86dc93..c7f10e1 100644 --- a/Functions/EQ_structures/vnle_postfilter_mlse.m +++ b/Functions/EQ_structures/vnle_postfilter_mlse.m @@ -45,7 +45,26 @@ end eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); % Process through postfilter and MLSE + [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); + +if 0 %tx_symbols.fs > 190e9 + if pf_.ncoeff == 1 + if pf_.coefficients(2) < 0 + % coeff is negative for too high/ bad VNLE convergence + pf_.coefficients(2) = 0.9; + + end + else + %long memory / pf respinse - not sure what to set here in a worst + %case :-) + + end + %do it again: + pf_.useBurg = 0; + [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); +end + mlse_.DIR = pf_.coefficients; GMI_MLSE = NaN; @@ -249,7 +268,8 @@ figure(336); hold on; eq_signal_sd.spectrum("displayname",'Equalized Signal','fignum',336,'normalizeTo0dB',0); eq_noise.spectrum("displayname",'Equalized Signal','fignum',336,'normalizeTo0dB',0); -showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients); + +showEQNoisePSD(eq_noise, "fignum", 338, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients); for t = 1:4 pf_.ncoeff = t; @@ -263,6 +283,7 @@ if ~isempty(postFFE) showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338); end +showEQcoefficients('n1', eq_.e,'n2', eq_.e2,'n3', eq_.e3, "displayname", 'Coefficients', 'fignum', 339); showEQfilter(eq_.e, eq_signal_sd.fs.*2); figure(340); clf; diff --git a/Functions/EQ_visuals/showEQNoisePSD.m b/Functions/EQ_visuals/showEQNoisePSD.m index 64f002d..ed7b440 100644 --- a/Functions/EQ_visuals/showEQNoisePSD.m +++ b/Functions/EQ_visuals/showEQNoisePSD.m @@ -22,7 +22,6 @@ end % Ensure the figure is ready before calling spectrum eq_noise.spectrum("displayname", options.displayname, "fignum", fig.Number, "normalizeTo0dB", 1,"color",options.color); - title('EEN') if ~isnan(options.postfilter_taps) % Hold on to the figure for further plotting @@ -43,6 +42,6 @@ end end xlim([-eq_noise.fs/2* 1e-9 eq_noise.fs/2* 1e-9]); - ylim([-15, 0]); + % ylim([-15, 0]); end diff --git a/Functions/EQ_visuals/showLevelHistogram.m b/Functions/EQ_visuals/showLevelHistogram.m index ef88791..e6e7156 100644 --- a/Functions/EQ_visuals/showLevelHistogram.m +++ b/Functions/EQ_visuals/showLevelHistogram.m @@ -52,6 +52,8 @@ end legend grid on + % view([90 -90]); + end diff --git a/Functions/Job_Processing/configureEqualizers_remove.m b/Functions/Job_Processing/configureEqualizers_remove.m deleted file mode 100644 index f207088..0000000 --- a/Functions/Job_Processing/configureEqualizers_remove.m +++ /dev/null @@ -1,40 +0,0 @@ -function [eq_, pf_, mlse_, mlse_db_, eq_post] = configureEqualizers(M, len_tr, vnle_order, dfe_order, mu_dc, mu_ffe, mu_dfe, pf_ncoeffs) - % CONFIGUREEQUALIZERS Creates and configures equalizer objects - % - % Inputs: - % M - PAM level - % len_tr - Training length - % vnle_order - Array with orders for VNLE [order1, order2, order3] - % dfe_order - Array with orders for DFE - % mu_dc - DC adaptation rate - % mu_ffe - Array with adaptation rates for FFE [mu1, mu2, mu3] - % mu_dfe - Adaptation rate for DFE - % pf_ncoeffs - Number of coefficients for postfilter - % - % Outputs: - % eq_ - Configured EQ object - % pf_ - Configured Postfilter object - % mlse_ - Configured MLSE_viterbi object - % mlse_db_ - Configured MLSE_viterbi object for duobinary - % eq_post - Configured FFE object for post-processing - - % Configure main equalizer - eq_ = EQ("Ne", vnle_order, "Nb", dfe_order, ... - "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); - - % Configure postfilter - pf_ = Postfilter("ncoeff", pf_ncoeffs, "useBurg", 1); - - % Configure MLSE objects - mlse_ = MLSE_viterbi("duobinary_output", 0, 'M', M, ... - 'trellis_states', PAMmapper(M,0).levels); - mlse_db_ = MLSE_viterbi("DIR", [1,1], "duobinary_output", 0, ... - "M", M, "trellis_states", PAMmapper(M,0).levels); - - % Configure post-FFE - eq_post = FFE("epochs_tr", 5, "epochs_dd", 5, "len_tr", 4096*2, ... - "mu_dd", 1e-4, "mu_tr", 0, "order", 2001, ... - "sps", 1, "decide", 0); - end \ No newline at end of file diff --git a/Functions/Job_Processing/preprocessSignal.m b/Functions/Job_Processing/preprocessSignal.m index 8725ed3..6932697 100644 --- a/Functions/Job_Processing/preprocessSignal.m +++ b/Functions/Job_Processing/preprocessSignal.m @@ -16,11 +16,17 @@ Scpe_sig = Scpe_sig.resample("fs_out", 2*fsym); [Scpe_sig, ~] = Scpe_sig.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0); % Apply Gaussian filter -% Scpe_sig = Filter('filtdegree', 4, "f_cutoff", Symbols.fs.*0.6, ... -% "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ... -% "active", true).process(Scpe_sig); +if 1 + Scpe_sig = Filter('filtdegree', 8, "f_cutoff", Symbols.fs.*0.52, ... + "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ... + "active", true).process(Scpe_sig); +else + Scpe_sig = Filter('filtdegree', 4, "f_cutoff", Symbols.fs.*0.6, ... + "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ... + "active", true).process(Scpe_sig); +end -% Remove DC offset +%Remove DC offset Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); end \ No newline at end of file diff --git a/Functions/Job_Processing/submitJobs.m b/Functions/Job_Processing/submitJobs.m index 321032a..5826814 100644 --- a/Functions/Job_Processing/submitJobs.m +++ b/Functions/Job_Processing/submitJobs.m @@ -209,6 +209,7 @@ wh = submit_options.wh; wh.addValueToStorageByLinIdx(val.vnle_package, 'vnle_package', jobIndex); wh.addValueToStorageByLinIdx(val.dbtgt_package,'dbtgt_package',jobIndex); wh.addValueToStorageByLinIdx(val.dbenc_package,'dbenc_package',jobIndex); + wh.addValueToStorageByLinIdx(val.mlmlse_package,'mlmlse_package',jobIndex); end end function p = setupParallelPool(numWorkers, idleTimeout) diff --git a/Functions/Lab_helper/loadFreqResp.m b/Functions/Lab_helper/loadFreqResp.m index 019ea07..e2c99e8 100644 --- a/Functions/Lab_helper/loadFreqResp.m +++ b/Functions/Lab_helper/loadFreqResp.m @@ -1,5 +1,5 @@ % Define the precomp path -precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\"; +precomp_path = "C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\precomp"; % Step 1: Find all valid files (assume .mat files for ChannelFreqResp) fileList = dir(fullfile(precomp_path, '*.mat')); diff --git a/Functions/Lab_helper/waitUntilClick.m b/Functions/Lab_helper/waitUntilClick.m index 6e06737..4aec3c2 100644 --- a/Functions/Lab_helper/waitUntilClick.m +++ b/Functions/Lab_helper/waitUntilClick.m @@ -1,20 +1,50 @@ -function waitUntilClick() - % Create the UI figure - fig = uifigure('Name', 'Pause Execution', 'Position', [100 100 300 150]); +function waitUntilClick(options) - % Create a label to inform the user - uilabel(fig, 'Position', [50 80 200 40], 'Text', 'Click "Continue" to proceed', ... - 'FontSize', 14, 'HorizontalAlignment', 'center'); +% use like this to stop any computation and wait for user to press ENTER or +% Continue button. I used this in the lab to wait until smth settled or I +% set a device to a certain operating point... - % Create the "Continue" button - continueButton = uibutton(fig, 'push', 'Text', 'Continue', 'Position', [100 20 100 40], ... - 'ButtonPushedFcn', @(src, event) closeWindow()); +% USAGE: +% waitUntilClick; + +% OPTIONAL: provide input text that is prompted :-) +% usrcmd = sprintf('Laser to %d dBm',p); +% waitUntilClick('Text',usrcmd); + + +arguments + options.Text string = "Click ""Continue"" to proceed" +end + +% Create the UI figure +fig = uifigure('Name', 'Pause Execution', ... + 'Position', [100 100 300 150], ... + 'KeyPressFcn', @(src,event) keyHandler(event)); + +% Create a label with custom text +uilabel(fig, ... + 'Position', [30 70 240 60], ... + 'Text', options.Text, ... + 'FontSize', 14, ... + 'HorizontalAlignment', 'center', ... + 'WordWrap','on'); + +% Create the "Continue" button +uibutton(fig, 'push', ... + 'Text', 'Continue', ... + 'Position', [100 20 100 40], ... + 'ButtonPushedFcn', @(src, event) closeWindow()); - % Function to close the window when "Continue" is clicked function closeWindow() - delete(fig); % Close the figure window + delete(fig); end - % Wait until the figure is closed to resume execution - waitfor(fig); -end \ No newline at end of file + function keyHandler(event) + if strcmp(event.Key,'return') || strcmp(event.Key,'enter') + closeWindow(); + end + end + +% Wait until the figure is closed +waitfor(fig); +end diff --git a/Functions/Metrics/noiseFigureFromOSNR.m b/Functions/Metrics/noiseFigureFromOSNR.m new file mode 100644 index 0000000..5491200 --- /dev/null +++ b/Functions/Metrics/noiseFigureFromOSNR.m @@ -0,0 +1,20 @@ +function NF_dB = noiseFigureFromOSNR(Pin_dBm, OSNR_dB, lambda_nm, Bref_nm) + + if nargin < 4 + Bref_nm = 0.1; % OSNR reference bandwidth + end + + h = 6.62607015e-34; % Planck [J*s] + c = 299792458; % speed of light [m/s] + + lambda = lambda_nm * 1e-9; + nu = c / lambda; + + % convert reference bandwidth from nm to Hz + Bref_Hz = Bref_nm*1e-9 * c / lambda^2; + + % ASE noise power density in dBm + Pn_dBm = 10*log10(h*nu*Bref_Hz) + 30; + + NF_dB = Pin_dBm - OSNR_dB - Pn_dBm; +end diff --git a/Functions/Theory/CCDM/ccdm_gpt_example.m b/Functions/Theory/CCDM/ccdm_gpt_example.m new file mode 100644 index 0000000..bea3bf3 --- /dev/null +++ b/Functions/Theory/CCDM/ccdm_gpt_example.m @@ -0,0 +1,27 @@ +%% Target source entropy for PS-PAM8 +clear; clc; + +M = 8; +a = -(M-1):2:(M-1); % PAM-8 amplitude levels: [-7 -5 -3 -1 1 3 5 7] +H_target = 2.79; % desired entropy [bits/symbol] + +% Objective: find nu such that H(PA) = H_target +f = @(nu) entropy_MB(a,nu) - H_target; +nu_opt = fzero(f, [0, 2]); % search ν in reasonable range + +% Compute final distribution +P = exp(-nu_opt*a.^2); +P = P/sum(P); +H = -sum(P .* log2(P)); + +fprintf('Shaping parameter ν = %.4f\n', nu_opt); +fprintf('Entropy H(A) = %.3f bits/symbol\n', H); +disp('Probability vector (P_A):'); +disp(P.'); + +%% Helper: entropy function +function H = entropy_MB(a,nu) + P = exp(-nu*a.^2); + P = P/sum(P); + H = -sum(P .* log2(P)); +end diff --git a/Functions/Theory/Neuer Ordner/duobinary_histogram.m b/Functions/Theory/Neuer Ordner/duobinary_histogram.m deleted file mode 100644 index a71396b..0000000 --- a/Functions/Theory/Neuer Ordner/duobinary_histogram.m +++ /dev/null @@ -1,51 +0,0 @@ - - - -figure(2) -tiledlayout(1,3) -cols = linspecer(5); -cnt = 1; -for m = [4,6,8] - - M = m; - fsym = 112e9; - fdac = 256e9; - - [Digi_sig,Symbols,Tx_bits] = PAMsource(... - "fsym",fsym,"M",M,"order",19,"useprbs",1,... - "fs_out",fdac,... - "applyclipping",0,"clipfactor",1.5,... - "applypulseform",0,"pulseformer",NaN,... - "randkey",33,... - "db_precode",1,"db_encode",0,... - "mrds_code",0,"mrds_blocklength",512).process(); - - Symbols_pre = Duobinary().precode(Symbols); - - Symbols_db = Duobinary().encode(Symbols_pre); - - if M == 4 - Symbols_db.signal = Symbols_db.signal .*sqrt(2.5); - elseif M == 6 - Symbols_db.signal = Symbols_db.signal .*sqrt(5.8); - elseif M == 8 - Symbols_db.signal = Symbols_db.signal .*sqrt(10.5); - end - - % figure(1) - % hold on - % histogram(Symbols_db.signal,"EdgeAlpha",0.3,"Normalization","probability"); - - - % figure(1) - nexttile - hold on - bar(unique(Symbols_db.signal),histcounts(int32(Symbols_db.signal),"Normalization","probability"),"FaceColor",cols(cnt,:),"FaceAlpha",0.6,"BarWidth",1-(0.2*cnt),"LineWidth",0.5,"EdgeColor",'black','DisplayName',['Duobinary PAM-',num2str(M)]); - xticks(unique(Symbols_db.signal)); - ylim([0 0.26]); - xlabel("Symbol") - - - cnt = cnt+1, - -end \ No newline at end of file diff --git a/Functions/Theory/Neuer Ordner/duobinary_transferfunction.m b/Functions/Theory/Neuer Ordner/duobinary_transferfunction.m deleted file mode 100644 index ccc7448..0000000 --- a/Functions/Theory/Neuer Ordner/duobinary_transferfunction.m +++ /dev/null @@ -1,18 +0,0 @@ - - -% Define the filter taps -h_ = {[1],[1 1],[1 2 1],[1 3 3 1]}; - -for i = 1:length(h_) - h = h_{i}; - - [H, w] = freqz(h, 1, 1024, 1); - - figure(1); - hold on - plot(w, 10*log10(abs(H)), 'LineWidth', 2,'DisplayName',['$(1+D)^2$']); %todo - xlabel('Normalized Frequency'); - ylabel('Amplitude in dB'); - grid on; - ylim([-20,10]) -end \ No newline at end of file diff --git a/Functions/Theory/dispersion_10km.m b/Functions/Theory/dispersion_10km.m new file mode 100644 index 0000000..83fcf29 --- /dev/null +++ b/Functions/Theory/dispersion_10km.m @@ -0,0 +1,89 @@ +%% ============================================================ +% IM/DD Fading Notch – λ_null vs. Bandwidth (Fixed 10 km) +% ============================================================ + +clear; clc; + +%% Fiber and dispersion parameters +lambda0 = 1310e-9; % Zero-dispersion wavelength [m] +S0 = 0.09; % Dispersion slope at ZDW [ps/(nm²·km)] +L = 10e3; % Fiber length [m] +c = physconst('lightspeed'); + +%% Frequency sweep (defines the desired first-fading notch) +f_targets = linspace(40e9, 150e9, 200); % [Hz] +f_GHz = f_targets / 1e9; + +%% Compute wavelength λ_null for each target f_null +[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0); +lambda_nm = lambda_vec * 1e9; % Convert to nm +Dacc = Dacc_vec; % [ps/nm] + +%% ------------------------------------------------------------ +% Plot λ_null vs. f_null for 10 km fiber +% ------------------------------------------------------------ +cols = cbrewer2('Paired',10); +figure('Color','w'); hold on; + +hLine = plot(lambda_nm, f_GHz, ... + 'LineWidth', 2, ... + 'DisplayName', sprintf('L = %.1f km', L/1000), ... + 'Color', cols(2,:)); + +xlabel('Wavelength λ [nm]'); +ylabel('First fading notch f_{null} [GHz]'); +title('IM/DD Fading Notch Position vs. Wavelength'); +grid on; box on; + +lim = (lambda0.*1e9) - [8, 40]; +xlim([lim(2) lim(1)]); +yticks([56,75,90,112]); + +%% ------------------------------------------------------------ +% Custom DataTip Template +% ------------------------------------------------------------ +% Add accumulated dispersion value to the DataTip +hLine.DataTipTemplate.DataTipRows(1).Label = 'λ [nm]'; +hLine.DataTipTemplate.DataTipRows(2).Label = 'f_{null} [GHz]'; + +% Create a new row for Dacc +dRow = dataTipTextRow('D_{acc} [ps/nm]', Dacc); +hLine.DataTipTemplate.DataTipRows(end+1) = dRow; + +%% ------------------------------------------------------------ +% Helper function: lambda_for_first_null_full +% ------------------------------------------------------------ +function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0) + c = physconst('lightspeed'); + S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³) + + lambda_min = 1260e-9; + lambda_max = 1360e-9; + + f_target = f_target(:); + N = numel(f_target); + + lambda_vec = zeros(N,1); + Dacc_vec = zeros(N,1); + + for k = 1:N + RHS = c * 0.5 / (f_target(k)^2 * L); + + fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS; + + try + lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]); + catch + lambda_sol = lambda_min; + end + + lambda_sol = min(max(lambda_sol, lambda_min), lambda_max); + lambda_vec(k) = lambda_sol; + + D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km) + Dacc_val = D_lambda * (L/1000); % ps/nm + Dacc_val = min(max(Dacc_val, -100), 100); + + Dacc_vec(k) = Dacc_val; + end +end diff --git a/Functions/Theory/dispersion_contour.m b/Functions/Theory/dispersion_contour.m index 3ed54aa..324dc2d 100644 --- a/Functions/Theory/dispersion_contour.m +++ b/Functions/Theory/dispersion_contour.m @@ -1,14 +1,15 @@ % Gitter für lambda0 und S0 -lambda0_vec = linspace(1300,1320,200); +lambda0_vec = linspace(1260,1360,200); S0_vec = linspace(0.06,0.1,200); [Lambda0, S0] = meshgrid(lambda0_vec, S0_vec); % Festen Betriebsparameter lambda = 1293; % nm -L = 10; % km +L = 1; % km % Dispersion berechnen (lineare Näherung) D = S0 .* ( lambda - Lambda0 ) * L; +% D = (S0./4) .* ( lambda - (Lambda0.^4)./(lambda^3) ) * L; %% 2D-Konturplot nur mit Linien und Text figure('Color','w'); diff --git a/Functions/Theory/dispersion_contour_bandwidth_lambda.m b/Functions/Theory/dispersion_contour_bandwidth_lambda.m new file mode 100644 index 0000000..1f46f0e --- /dev/null +++ b/Functions/Theory/dispersion_contour_bandwidth_lambda.m @@ -0,0 +1,146 @@ +%% ------------------------------------------------------------ +% Contour plot: λ_null as function of bandwidth (f_target) and reach (L) +% ------------------------------------------------------------ + +% Parameters +lambda0 = 1310e-9; % [m] +S0 = 0.08; % [ps/(nm²·km)] +c = physconst('lightspeed'); + +% Sweep dimensions +f_targets = linspace(50e9, 120e9, 100); % [Hz] (x-axis) +L_values = linspace(0.5e3, 10e3, 100); % [m] (y-axis) + +lambda_surface = zeros(numel(L_values), numel(f_targets)); +Dacc_surface = zeros(numel(L_values), numel(f_targets)); + +% Outer loop over fiber length (since L must be scalar) +for iL = 1:numel(L_values) + L = L_values(iL); + [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0); + lambda_vec = 2*abs(lambda0 - lambda_vec); + + if 0 + fprintf('\n- %d km ------------------------------------\n',L); + fprintf(' f_null [GHz] lambda [nm] Dacc [ps/nm]\n'); + fprintf('----------------------------------------------\n'); + fprintf('%10.1f %8.2f %+8.3f\n',[f_targets(:)/1e9, lambda_vec(:)*1e9, Dacc_vec(:)].'); + fprintf('----------------------------------------------\n\n'); + end + + lambda_surface(iL, :) = lambda_vec; % λ for each f_target + Dacc_surface(iL, :) = Dacc_vec; % corresponding accumulated dispersion +end + +% Convert for plotting +lambda_surface_nm = lambda_surface * 1e9; % [nm] +L_km = L_values / 1000; % [km] +f_GHz = f_targets / 1e9; % [GHz] + +%% Contour plot +figure('Color','w'); + +% Define wavelength contour levels [nm] +lambda_levels = [1260:10:1290, 1290:5:1300, 1300:2.5:1310]; +lambda_levels = [100:-20:50, 50:-10:30,30:-5:0]; + +% Contour plot +contour(f_GHz, L_km, lambda_surface_nm, lambda_levels, ... + 'LineWidth', 1.5, ... + 'ShowText', 'on', ... + 'LabelFormat', '%.0f nm'); + +% Colormap and colorbar +colormap((cbrewer2('RdYlGn',100))); +colorbar; +clim([0 100]); + +% Axis formatting +xlabel('Signal Bandwidth [GHz]'); +ylabel('Fiber length [km]'); +legend('$\Delta \lambda$') + +% X-axis ticks at 56 : 16 : 150 GHz +xticks(56:8:150); + +grid on; box on; + + +%% Optional: overlay accumulated-dispersion contours +if 0 + hold on; + [CS, h] = contour(f_GHz, L_km, Dacc_surface, 10, 'k--', 'LineWidth', 0.8); + clabel(CS, h, 'Color','k', 'FontSize',8); +end + +function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0) +% lambda_for_first_null_full (stable, single-branch + validity checks) +% -------------------------------------------------------------------- +% Computes the wavelength(s) at which the first IM/DD fading null +% occurs at frequency/ies f_target using the full dispersion model: +% +% D(lambda) = (S0/4)*(lambda - lambda0^4 / lambda^3) +% +% Restricted to the NORMAL-dispersion branch (λ < λ0), +% and valid only in the O-band (1260–1360 nm). +% +% Inputs: +% f_target - scalar or vector of target null frequencies [Hz] +% L - fiber length [m] +% lambda0 - zero-dispersion wavelength (ZDW) [m] +% S0 - dispersion slope at ZDW [ps/(nm²·km)] +% +% Outputs: +% lambda_vec - wavelength(s) [m] where first null occurs (clamped to O-band) +% Dacc_vec - accumulated dispersion(s) [ps/nm] (NaN if out of valid range) +% -------------------------------------------------------------------- + + c = physconst('lightspeed'); + S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³) + + % Define O-band boundaries (in meters) + lambda_min = 1255e-9; + lambda_max = 1361e-9; + + % Force column vector + f_target = f_target(:); + N = numel(f_target); + + lambda_vec = NaN(N,1); + Dacc_vec = NaN(N,1); + + for k = 1:N + RHS = c * 0.5 / (f_target(k)^2 * L); + + % Normal-dispersion branch (λ < λ0) + fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS; + + % Limit the search to [λ_min, λ0) + try + lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]); + catch + % If the zero is not within bounds, skip this point + lambda_sol = NaN; + end + + % Validate solution + if isnan(lambda_sol) || lambda_sol < lambda_min || lambda_sol > lambda_max + lambda_vec(k) = NaN; + Dacc_vec(k) = NaN; + continue + end + + % Compute D(lambda) and accumulated dispersion + D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km) + Dacc_val = D_lambda * (L/1000); % ps/nm + + % Sanity bound on dispersion (avoid unphysical > ±100 ps/nm) + if abs(Dacc_val) > 100 + lambda_vec(k) = NaN; + Dacc_vec(k) = NaN; + else + lambda_vec(k) = lambda_sol; + Dacc_vec(k) = Dacc_val; + end + end +end diff --git a/Functions/Theory/dispersion_first_notch_10km.m b/Functions/Theory/dispersion_first_notch_10km.m new file mode 100644 index 0000000..96a2149 --- /dev/null +++ b/Functions/Theory/dispersion_first_notch_10km.m @@ -0,0 +1,38 @@ +%% ------------------------------------------------------------ +% Plot: Maximum usable IM/DD bandwidth vs wavelength +% ------------------------------------------------------------ + +% Fiber and dispersion parameters +lambda0 = 1310e-9; % [m] +S0 = 0.08; % [ps/(nm²·km)] +L = 10000; % [m] +c = physconst('lightspeed'); + +% Wavelength range around ZDW +lambda_vec = linspace(1250e-9, 1350e-9, 200); % [m] + +% Compute D(lambda) using full model +lambda_nm = lambda_vec * 1e9; +lambda0_nm = lambda0 * 1e9; +D_lambda = (S0/4) .* (lambda_nm - (lambda0_nm.^4) ./ (lambda_nm.^3)); % [ps/(nm·km)] + +% Convert D to [s/m²] +D_si = D_lambda * 1e-6; + +% Compute first null frequency (f₀) for each wavelength +f_null = sqrt(c*(0.5) ./ (abs(D_si).*lambda_vec.^2*L)); % [Hz] + +% Plot +figure('Color','w'); +plot(lambda_vec*1e9, f_null/1e9, 'LineWidth', 1.6); +grid on; box on; +xlabel('Wavelength [nm]'); +ylabel('First Fading Null Frequency [GHz]'); +title(sprintf('IM/DD Bandwidth Limit vs. Wavelength (L = %.1f km)', L/1000)); + +% Highlight useful bandwidth thresholds +yline(25, '--', '25 GHz','Color',[0.4 0.4 0.4],'LabelHorizontalAlignment','left'); +yline(50, '--', '50 GHz','Color',[0.2 0.6 0.2],'LabelHorizontalAlignment','left'); +yline(100,'--', '100 GHz','Color',[0.6 0.2 0.2],'LabelHorizontalAlignment','left'); + +legend('First fading notch (f_{null})','Location','best'); diff --git a/Functions/Theory/dispersion_power_fading.m b/Functions/Theory/dispersion_power_fading.m new file mode 100644 index 0000000..6ed8fab --- /dev/null +++ b/Functions/Theory/dispersion_power_fading.m @@ -0,0 +1,165 @@ +%% Chromatic Dispersion Power Fading Demonstration +% ------------------------------------------------------------ +% This script computes and visualizes power fading after +% photodiode detection caused by chromatic dispersion in IM/DD links. +% +% It also determines the wavelength λ that produces the first +% fading null at a specified RF frequency f_target using the +% full physical dispersion model: +% +% D(λ) = (S0/4) * (λ - λ0^4 / λ^3) +% +% and compares the analytic null frequency with simulation. +% ------------------------------------------------------------ + +% clear; close all; clc; + +%% Fiber and wavelength parameters +lambda0 = 1310e-9; % Zero-dispersion wavelength (ZDW) [m] +S0 = 0.08; % Dispersion slope at ZDW [ps/(nm^2·km)] +L = 10000; % Fiber length [m] +alpha_dB = 0; % Attenuation [dB/m] (ignored here) + +%% Target null frequency +f_targets = linspace(55e9,58e9,10); +f_targets = 56e9; +% f_targets = 80e9; +% Compute wavelength that gives the first null at f_target +[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0); +% lambda_vec = 1293e-9; + + +fprintf('\n----------------------------------------------\n'); +fprintf(' f_null [GHz] lambda [nm] Dacc [ps/nm]\n'); +fprintf('----------------------------------------------\n'); +fprintf('%10.1f %8.2f %+8.3f\n',[f_targets(:)/1e9, lambda_vec(:)*1e9, Dacc_vec(:)].'); +fprintf('----------------------------------------------\n\n'); + +%% Frequency grid +f_simu = 500e9; % Simulation bandwidth [Hz] +N_freq = 500000; +faxis = linspace(-f_simu/2, f_simu/2, N_freq); + +%% Derived fiber parameters +c = physconst('lightspeed'); +S0_si = S0 * 1e3; % ps/(nm²·km) -> s/m³ + +% Convert wavelengths to nm for the D(lambda) model +lambda_nm = lambda_vec(end) * 1e9; +lambda0_nm = lambda0 * 1e9; + +% Dispersion parameter [ps/(nm·km)] +D_lambda = (S0/4) * (lambda_nm - (lambda0_nm^4)/(lambda_nm^3)); + +% Convert to [s/m²] +D_si = D_lambda * 1e-6; + +% β2 in [s²/m] +b2 = -D_si * lambda_vec(end)^2 / (2*pi*c); + +%% IM/DD intensity response (simulation) +phi = 2*pi^2*b2*faxis.^2*L; +H_field_pos = exp(-1j*phi); % +f sideband +H_field_neg = exp(+1j*phi); % -f sideband +H_intensity = 0.5 * (H_field_pos + H_field_neg); % PD beating term +H_sim = abs(H_intensity); + +%% Theoretical analytical IM/DD response +phi = 2*pi^2 * abs(b2) * faxis.^2 * L; +H_theoretical = abs(cos(phi)); + +%% Analytic first null (for verification) +f_null_analytic = sqrt(c*(0.5)/(abs(D_si)*lambda_vec(end)^2*L)); +fprintf('Analytic first null from D,λ,L: %.2f GHz\n\n', f_null_analytic/1e9); + +%% Plot +cols = linspecer(5); +figure('Color','w'); hold on; grid on; box on; +plot(faxis*1e-9, 10*log10(H_sim), 'DisplayName','$|H_{sim}|$ (IM/DD simulation)','Color',cols(1,:)); +plot(faxis*1e-9, 10*log10(H_theoretical), 'DisplayName','|cos($\phi$)| (theory)','Color',cols(2,:),'LineStyle','--'); +xline(f_targets(end)/1e9,'k:','LineWidth',1.2,'DisplayName','Target null (56 GHz)'); +xline(f_null_analytic/1e9,'Color',[0.2 0.6 0.2],'LineStyle','-.','LineWidth',1.2,'DisplayName','Analytic null'); +xlabel('Frequency [GHz]'); +ylabel('Magnitude [dB]'); +title(sprintf('Power Fading for %.2f nm, L = %.1f km',lambda_nm,L/1000)); +legend('Location','best'); ylim([-30 0]); + +%% Plot Bandwidth vs Lambda max + +figure(); +hold on; +plot(lambda_vec.*1e6,f_targets.*1e-9) +xlabel('wavelength'); +ylabel('max. Bandwidth') + +function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0) +% lambda_for_first_null_full (stable, single-branch + validity checks) +% -------------------------------------------------------------------- +% Computes the wavelength(s) at which the first IM/DD fading null +% occurs at frequency/ies f_target using the full dispersion model: +% +% D(lambda) = (S0/4)*(lambda - lambda0^4 / lambda^3) +% +% Restricted to the NORMAL-dispersion branch (λ < λ0), +% and valid only in the O-band (1260–1360 nm). +% +% Inputs: +% f_target - scalar or vector of target null frequencies [Hz] +% L - fiber length [m] +% lambda0 - zero-dispersion wavelength (ZDW) [m] +% S0 - dispersion slope at ZDW [ps/(nm²·km)] +% +% Outputs: +% lambda_vec - wavelength(s) [m] where first null occurs (clamped to O-band) +% Dacc_vec - accumulated dispersion(s) [ps/nm] (NaN if out of valid range) +% -------------------------------------------------------------------- + + c = physconst('lightspeed'); + S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³) + + % Define O-band boundaries (in meters) + lambda_min = 1255e-9; + lambda_max = 1361e-9; + + % Force column vector + f_target = f_target(:); + N = numel(f_target); + + lambda_vec = NaN(N,1); + Dacc_vec = NaN(N,1); + + for k = 1:N + RHS = c * 0.5 / (f_target(k)^2 * L); + + % Normal-dispersion branch (λ < λ0) + fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS; + + % Limit the search to [λ_min, λ0) + try + lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]); + catch + % If the zero is not within bounds, skip this point + lambda_sol = NaN; + end + + % Validate solution + if isnan(lambda_sol) || lambda_sol < lambda_min || lambda_sol > lambda_max + lambda_vec(k) = NaN; + Dacc_vec(k) = NaN; + continue + end + + % Compute D(lambda) and accumulated dispersion + D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km) + Dacc_val = D_lambda * (L/1000); % ps/nm + + % Sanity bound on dispersion (avoid unphysical > ±100 ps/nm) + if abs(Dacc_val) > 100 + lambda_vec(k) = NaN; + Dacc_vec(k) = NaN; + else + lambda_vec(k) = lambda_sol; + Dacc_vec(k) = Dacc_val; + end + end +end \ No newline at end of file diff --git a/Functions/Theory/dispersion_wavelength_notch.m b/Functions/Theory/dispersion_wavelength_notch.m new file mode 100644 index 0000000..0f4da7e --- /dev/null +++ b/Functions/Theory/dispersion_wavelength_notch.m @@ -0,0 +1,32 @@ +%% Dependency f_null vs Delta_lambda +lambda0 = 1310e-9; +S0 = 0.09; % ps/(nm²·km) +L = 10e3; % m +c = physconst('lightspeed'); + +% Convert slope to SI +S0_si = S0 * 1e3; % s/m³ + +Delta_lambda = linspace(5e-9, 80e-9, 300); % [m] detuning +f_null_2 = sqrt( c * 0.5 ./ (S0_si .* abs(Delta_lambda) .* lambda0.^2 .* L) ); +L = 2e3; % m +f_null_10 = sqrt( c * 0.5 ./ (S0_si .* abs(Delta_lambda) .* lambda0.^2 .* L) ); + +cols = cbrewer2('Paired',10); +figure('Color','w');hold on +cnt = 2; +for L = 10%[2,5,10] + f_null_10 = sqrt( c * 0.5 ./ (S0_si .* abs(Delta_lambda) .* lambda0.^2 .* L*1e3) ); + plot(1310-Delta_lambda*1e9, f_null_10/1e9, 'LineWidth',2,'DisplayName',sprintf('%d km',L),'Color',cols(cnt,:)); + cnt = cnt+2; +end +yticks([56,75,90,112]) +tickse = 1310-[7.5, 12, 17, 31.5]; +xticks(flip(tickse)); + +xlabel('$\Delta \lambda$ from ZDW [nm]'); +ylabel('$F_{null}$ [GHz]'); +grid on; box on; +lim=1310-[5,35]; +xlim([lim(2) lim(1)]); +ylim([40,130]) \ No newline at end of file diff --git a/Functions/Theory/dispersion_wdm.m b/Functions/Theory/dispersion_wdm.m new file mode 100644 index 0000000..f9ead0c --- /dev/null +++ b/Functions/Theory/dispersion_wdm.m @@ -0,0 +1,111 @@ +%% ============================================================ +% IM/DD Fading Notch Design Map +% Shows λ_null vs. bandwidth (f_target) and fiber length (L) +% ============================================================ + +clear; close all; clc; + +%% Parameters +lambda0 = 1310e-9; % Zero-dispersion wavelength [m] +S0 = 0.08; % Dispersion slope at ZDW [ps/(nm²·km)] +c = physconst('lightspeed'); + +% Frequency and length sweep +f_targets = linspace(20e9, 140e9, 80); % [Hz] → x-axis +L_values = linspace(0.5e3, 12e3, 80); % [m] → y-axis + +% Preallocate result matrices +lambda_surface = zeros(numel(L_values), numel(f_targets)); +Dacc_surface = zeros(numel(L_values), numel(f_targets)); + +%% Compute λ_null and Dacc for each (f_target, L) +for iL = 1:numel(L_values) + L = L_values(iL); + [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0); + lambda_surface(iL, :) = lambda_vec; % [m] + Dacc_surface(iL, :) = Dacc_vec; % [ps/nm] +end + +%% Convert to display units +lambda_surface_nm = lambda_surface * 1e9; % [nm] +L_km = L_values / 1000; % [km] +f_GHz = f_targets / 1e9; % [GHz] + +%% ------------------------------------------------------------ +% Contour plot (λ_null as function of f_null and L) +% ------------------------------------------------------------ +figure('Color','w'); + +% Define wavelength contour levels [nm] +lambda_levels = [1260:10:1290, 1290:5:1300, 1300:2:1310]; + +contourf(f_GHz, L_km, lambda_surface_nm, lambda_levels, ... + 'LineWidth', 1.5, ... + 'ShowText', 'on', ... + 'LabelFormat', '%1.1d nm'); + +% Colormap and colorbar +colormap(flip(cbrewer2('RdYlGn',100))); +clim([1260 1310]); +% c = colorbar; +% ylabel(c, 'λ_{null} [nm]', 'Rotation', 90); + +% Axis formatting +xlabel('Signal Bandwidth [GHz]'); +ylabel('Fiber length L [km]'); +% X-axis ticks (every 16 GHz starting at 56 GHz) +xticks(56:8:120); +xlim([56,120]) +grid on; box on; + +%% Optional overlay: accumulated dispersion contours +hold on; +[CS, h] = contour(f_GHz, L_km, Dacc_surface, 10, 'k--', 'LineWidth', 0.8); +clabel(CS, h, 'Color','k', 'FontSize',8); +legend('λ_{null} contours','|D_{acc}| [ps/nm]','Location','best'); + +%% ============================================================ +% Helper function: lambda_for_first_null_full +% Stable, single-branch, clamped to O-band +% ============================================================ +function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0) + c = physconst('lightspeed'); + S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³) + + % Define O-band boundaries (in meters) + lambda_min = 1260e-9; + lambda_max = 1360e-9; + + % Force column vector + f_target = f_target(:); + N = numel(f_target); + + lambda_vec = zeros(N,1); + Dacc_vec = zeros(N,1); + + for k = 1:N + RHS = c * 0.5 / (f_target(k)^2 * L); + + % Normal-dispersion branch (λ < λ0) + fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS; + + % Solve within the normal-dispersion range + try + lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]); + catch + lambda_sol = lambda_min; + end + + % Clamp to O-band range + lambda_sol = min(max(lambda_sol, lambda_min), lambda_max); + lambda_vec(k) = lambda_sol; + + % Compute D(lambda) and accumulated dispersion + D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km) + Dacc_val = D_lambda * (L/1000); % ps/nm + + % Clamp to physical range + Dacc_val = min(max(Dacc_val, -100), 100); + Dacc_vec(k) = Dacc_val; + end +end diff --git a/Functions/Theory/matched_filter_rrc.m b/Functions/Theory/matched_filter_rrc.m new file mode 100644 index 0000000..577ec10 --- /dev/null +++ b/Functions/Theory/matched_filter_rrc.m @@ -0,0 +1,120 @@ +%% Matched Filter SNR Demonstration (Correct Timing) +% clear; close all; clc; + +%% Parameters +M = 4; % QPSK +numSymbols = 1e6; +sps = 25; % samples per symbol +rolloff = 0.5; +EbNo_dB = 10; + +%% Generate random data +data = randi([0 M-1], numSymbols, 1); +txSym = qammod(data, M, 'UnitAveragePower', true); + +%% Root Raised Cosine filters +span = 64; % filter span in symbols +rrcTx = rcosdesign(rolloff, span, sps, 'sqrt'); +rrcRx = rrcTx; % matched filter + +txSignal2 = ifft(fft(rrcTx).*fft(txSym)); + +%% Transmit filtering (includes upsampling) +txSignal = upfirdn(txSym, rrcTx, sps, 1); + +%% AWGN channel +rxSignal = awgn(txSignal, EbNo_dB + 10*log10(sps), 'measured'); + +%% Receiver matched filter +rxFilt = conv(rxSignal, rrcRx, 'same'); + +%% Symbol timing (group delay compensation) +delay = span * sps / 2; % total delay per filter is span*sps/2 +rxAligned = rxFilt(delay+1 : end-delay); + +%% Downsample to symbol rate +rxSampled = rxAligned(1:sps:end); + +%% Align lengths +L = min(length(rxSampled), length(txSym)); +rxSampled = rxSampled(1:L); +txSym = txSym(1:L); + +%% Decision and BER +rxSym = qamdemod(rxSampled, M, 'UnitAveragePower', true); +[~, ber] = biterr(data(1:L), rxSym); + +%% Compute effective SNR +snr_meas = 10*log10(mean(abs(txSym).^2) / mean(abs(txSym - rxSampled).^2)); + +fprintf('Measured BER: %.3e | Effective SNR: %.2f dB\n', ber, snr_meas); + + +%% Eye diagrams +eyediagram(rxSignal(1:4000), 2*sps); +title('Received Signal (Before Matched Filter)'); +eyediagram(rxFilt(1:4000), 2*sps); +title('After Matched Filter (RRC)'); + +%% -------------------------------------------------------------- +%% Spectrum analysis of shaped and filtered signals +%% -------------------------------------------------------------- + +Fs = sps; % normalized sample rate (symbol rate = 1) +Nfft = 2^16; % FFT size for high resolution +f = (-Nfft/2:Nfft/2-1)/Nfft * Fs; % normalized frequency axis (symbol-rate units) + +% Spectra +S_tx = 20*log10(abs(fftshift(fft(txSignal, Nfft)))/max(abs(fft(txSignal, Nfft)))); +S_rx = 20*log10(abs(fftshift(fft(rxFilt, Nfft)))/max(abs(fft(rxFilt, Nfft)))); + +% Unshaped (rectangular pulse) for comparison +txRect_unf = upfirdn(txSym, ones(1, sps), sps, 1); +S_rect = 20*log10(abs(fftshift(fft(txRect_unf, Nfft)))/max(abs(fft(txRect_unf, Nfft)))); + +% Plot +figure('Name','Spectrum after Pulse Shaping'); +plot(f, S_rect, '--', 'DisplayName','Rectangular pulse'); +hold on; +plot(f, S_tx, 'LineWidth',1.4, 'DisplayName','RRC (TX)'); +plot(f, S_rx, 'LineWidth',1.4, 'DisplayName','After Matched Filter'); +grid on; +xlabel('Normalized frequency (× symbol rate)'); +ylabel('Magnitude [dB]'); +title('Spectra Before and After RRC Pulse Shaping'); +legend('Location','best'); +xlim([-1.5 1.5]); +ylim([-60 0]); + + +%% -------------------------------------------------------------- +%% Visualization: RRC and Raised-Cosine Frequency Responses +%% -------------------------------------------------------------- + +% Frequency axis for plotting (normalized to symbol rate) +Nfft = 4096; +H_rrc = fftshift(fft(rrcTx, Nfft)); +H_rc = H_rrc .* H_rrc; % cascade of TX and RX RRC = full RC + +f = linspace(-0.5, 0.5, Nfft); % normalized frequency (symbol-rate units) + +figure('Name','Raised Cosine Filter Characteristics'); + +subplot(2,1,1); +plot(f, 20*log10(abs(H_rrc)/max(abs(H_rrc))), 'LineWidth', 1.5); +hold on; +plot(f, 20*log10(abs(H_rc)/max(abs(H_rc))), '--', 'LineWidth', 1.5); +grid on; +xlabel('Normalized frequency (× symbol rate)'); +ylabel('Magnitude [dB]'); +title(sprintf('RRC (rolloff = %.2f) and Full RC Spectrum', rolloff)); +legend('Root Raised Cosine','Raised Cosine (TX×RX)','Location','best'); +ylim([-60 5]); + +subplot(2,1,2); +t = (-span*sps/2 : span*sps/2) / sps; % time axis in symbol durations +plot(t, rrcTx, 'LineWidth', 1.5); +grid on; +xlabel('Time [symbols]'); +ylabel('Amplitude'); +title('RRC Impulse Response'); diff --git a/Functions/Theory/power_fading.m b/Functions/Theory/power_fading.m new file mode 100644 index 0000000..0e14663 --- /dev/null +++ b/Functions/Theory/power_fading.m @@ -0,0 +1,40 @@ +%% ============================================================ +% Minimal IM/DD Power Fading Plot +% ============================================================ + + +%% Fiber and system parameters +lambda0 = 1310e-9; % zero-dispersion wavelength [m] +lambda = 1275e-9; % operating wavelength [m] +S0 = 0.08; % dispersion slope [ps/(nm²·km)] +L = 10e3; % fiber length [m] +c = physconst('lightspeed'); + +%% Derived quantities +S0_si = S0 * 1e3; % → s/m³ +D_lambda = (S0/4) * (lambda*1e9 - (lambda0*1e9)^4/(lambda*1e9)^3); % ps/(nm·km) +D_si = D_lambda * 1e-6; % → s/m² +b2 = -D_si * lambda^2 / (2*pi*c); % s²/m + +%% Frequency grid +f_max = 150e9; +f = linspace(0, f_max, 4000); % [Hz] + +%% IM/DD transfer function (power fading) +phi = 2*pi^2 * b2 * f.^2 * L; +H = abs(cos(phi)); + +%% Plot +figure('Color','w'); +plot(f/1e9, 10*log10(H), 'LineWidth', 1.8); +grid on; box on; +xlabel('Frequency [GHz]'); +ylabel('Magnitude [dB]'); +title(sprintf('IM/DD Power Fading |H| for λ = %.1f nm, L = %.1f km', lambda*1e9, L/1000)); +ylim([-30 0]); + +%% Mark analytic first-null frequency +f_null = sqrt(c*(0.5)/(abs(D_si)*lambda^2*L)); +xline(f_null/1e9, 'r--', 'LineWidth', 1.2, ... + 'Label', sprintf('f_{null}=%.1f GHz', f_null/1e9), ... + 'LabelOrientation', 'horizontal', 'LabelVerticalAlignment', 'bottom'); diff --git a/Functions/beautifyBERplot.m b/Functions/beautifyBERplot.m index 0d0a665..9bc5d0a 100644 --- a/Functions/beautifyBERplot.m +++ b/Functions/beautifyBERplot.m @@ -1,37 +1,113 @@ -function beautifyBERplot() - % BEAUTIFYBERPLOT Enhances a BER plot for publication-quality figures. +function beautifyBERplot(options) +% BEAUTIFYBERPLOT Enhances BER-style plots for publication-quality figures. +% Supports automatic smoothing and trend-line overlay. +% +% Usage examples: +% beautifyBERplot; % default +% beautifyBERplot("polyfit",1); % add polynomial fit +% beautifyBERplot("polyfit",1,"fitmethod","pchip") % piecewise cubic fit +% +% Supported fitmethod options: 'polyfit', 'smoothingspline', 'loess', 'pchip' + +arguments + options.logscale (1,1) logical = 1 + options.setmarkers (1,1) logical = 1; + options.polyfit (1,1) logical = 0 + options.polyorder (1,1) double = 2 + options.fitmethod (1,1) string = "polyfit" % choose fit type +end + +% --- find all line objects in current axes +lines = findall(gca, 'Type', 'Line'); +markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'}; +num_markers = length(markers); + +% --- style all lines consistently +for i = 1:length(lines) + lines(i).LineWidth = 1.2; + % lines(i).LineStyle = '-'; + if options.setmarkers == 1 + if string(lines(i).Marker) == "none" + lines(i).Marker = markers{mod(i-1, num_markers) + 1}; + end + lines(i).MarkerSize = 4; + lines(i).MarkerFaceColor = lines(i).Color; + end +end + +% --- optional smoothing/fitting overlay +if options.polyfit + hold on + for i = 1:length(lines) + x = lines(i).XData; + y = lines(i).YData; + valid = isfinite(x) & isfinite(y); + if sum(valid) < options.polyorder + 1 + continue; + end + + xf = linspace(min(x(valid)), max(x(valid)), 200); + + % ----- choose fitting method ----- + switch lower(options.fitmethod) + case "polyfit" + p = polyfit(x(valid), y(valid), options.polyorder); + yf = polyval(p, xf); + + case "smoothingspline" + try + f = fit(x(valid)', y(valid)', 'smoothingspline'); + yf = feval(f, xf); + catch + yf = interp1(x(valid), y(valid), xf, 'pchip'); + end + + case "loess" + yf = smooth(x(valid), y(valid), 0.2, 'loess'); + yf = interp1(x(valid), yf, xf, 'linear', 'extrap'); + + case "pchip" + yf = interp1(x(valid), y(valid), xf, 'pchip'); + + otherwise + warning('Unknown fitmethod "%s". Using polyfit.', options.fitmethod); + p = polyfit(x(valid), y(valid), options.polyorder); + yf = polyval(p, xf); + end + + % --- lightened color for fit overlay + lightcol = lines(i).Color + 0.4 * (1 - lines(i).Color); + lightcol(lightcol > 1) = 1; + + plot(xf, yf, '-', 'Color', lightcol, ... + 'LineWidth', 0.7, 'Marker', 'none', ... + 'HandleVisibility','off'); + end + hold off +end + +% --- axis scaling and cosmetics +if options.logscale + set(gca, 'YScale', 'log'); +end + +% --- Figure size in centimeters --- +% width_pt = 500; +% height_pt = 300; +% +% pt2cm = 0.03514598; % TeX point → cm +% width_cm = width_pt * pt2cm; % = 8.85 cm +% height_cm = height_pt * pt2cm; % = 2.81 cm +% +% set(gcf, 'Units', 'centimeters', 'Position', [2 2 width_cm height_cm]); +% set(gcf, 'PaperUnits', 'centimeters', 'PaperPosition', [0 0 width_cm height_cm]); + +% --- Formatting --- +set(findall(gca, '-property', 'Interpreter'), 'Interpreter', 'latex'); +set(gcf, 'Color', 'w'); +set(gca, 'Box', 'on', 'LineWidth', 0.8); +grid on; +set(gca, 'FontSize', 10, 'FontName', 'Latin Modern Roman'); + - % Set line properties for all current plot lines - lines = findall(gca, 'Type', 'Line'); - markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'}; % Define marker styles - num_markers = length(markers); - - for i = 1:length(lines) - lines(i).LineWidth = 1.3; % Thicker line width - %lines(i).LineStyle = '-'; % Solid lines for simplicity - % if string(lines(i).Marker) == "none" - % lines(i).Marker = markers{mod(i-1, num_markers) + 1}; % Assign markers cyclically - % end - lines(i).MarkerSize = 4; % Marker size - lines(i).MarkerFaceColor = 'auto'; % Use line color for marker face - end - - % Change all text interpreters to LaTeX - set(findall(gca, '-property', 'Interpreter'), 'Interpreter', 'latex'); - - % Set figure background to white - set(gcf, 'Color', 'w'); - - - % Set logarithmic scale for y-axis, but only if it makes sense. - % If this is not always desired, you could condition this on the presence of lines or data. - set(gca, 'YScale', 'log'); - - % Customize grid and box appearance - set(gca, 'Box', 'on', 'LineWidth', 0.8); % Thicker border - grid on; - % grid minor; - - % Adjust font size and style for better readability - set(gca, 'FontSize', 10, 'FontName', 'Times New Roman'); end diff --git a/Functions/convert_freq_lambda.m b/Functions/convert_freq_lambda.m new file mode 100644 index 0000000..e65f569 --- /dev/null +++ b/Functions/convert_freq_lambda.m @@ -0,0 +1,62 @@ +%% ============================================================ +% Wavelength–Frequency Conversion Utilities +% ============================================================ + +% Example usage: +% f = lambda2freq(1310e-9); % 1310 nm -> Hz +% lambda = freq2lambda(224e12); % 224 THz -> m +% delta_lambda_nm = df2dlambda(224e12, 400e9); % 400 GHz @ 224 THz -> nm +% delta_freq_GHz = dlambda2df(1310e-9, 3.45); % 3.45 nm @ 1310 nm -> GHz + +%% ---- Core conversion functions ---- +function f = lambda2freq(lambda) +% lambda2freq Convert wavelength [m] → frequency [Hz] + c = physconst('lightspeed'); + f = c ./ lambda; +end + +function lambda = freq2lambda(f) +% freq2lambda Convert frequency [Hz] → wavelength [m] + c = physconst('lightspeed'); + lambda = c ./ f; +end + +%% ---- Differential conversions ---- +function d_lambda = df2dlambda(f_center, d_f) +% df2dlambda Convert frequency spacing Δf [Hz] → wavelength spacing Δλ [m] +% around a given center frequency f_center [Hz]. +% Uses first-order differential: Δλ ≈ (c / f^2) * Δf + + c = physconst('lightspeed'); + d_lambda = (c ./ (f_center.^2)) .* d_f; +end + +function d_f = dlambda2df(lambda_center, d_lambda) +% dlambda2df Convert wavelength spacing Δλ [m] → frequency spacing Δf [Hz] +% around a given center wavelength λ_center [m]. +% Uses first-order differential: Δf ≈ (c / λ^2) * Δλ + + c = physconst('lightspeed'); + d_f = (c ./ (lambda_center.^2)) .* d_lambda; +end + +%% ============================================================ +% Example section (can be commented out) +% ============================================================ + +if ~isdeployed + fprintf('--- Example conversions ---\n'); + + lambda_nm = 1310; % nm + lambda = lambda_nm * 1e-9; % m + f = lambda2freq(lambda); % Hz + fprintf('λ = %.1f nm → f = %.3f THz\n', lambda_nm, f/1e12); + + d_f = 2000e9; % 400 GHz spacing + d_lambda = df2dlambda(f, d_f); % [m] + fprintf('Δf = %.0f GHz @ %.1f nm → Δλ = %.3f nm\n', d_f/1e9, lambda_nm, d_lambda*1e9); + + % Verify reverse direction + d_f_back = dlambda2df(lambda, d_lambda); + fprintf('Δλ = %.3f nm @ %.1f nm → Δf = %.0f GHz\n', d_lambda*1e9, lambda_nm, d_f_back/1e9); +end diff --git a/Libs/wesanderson_colors/WesPalette.m b/Libs/wesanderson_colors/WesPalette.m new file mode 100644 index 0000000..7cd1dcb --- /dev/null +++ b/Libs/wesanderson_colors/WesPalette.m @@ -0,0 +1,133 @@ +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 + + enumeration + BottleRocket1 + BottleRocket2 + Rushmore1 + Rushmore + Royal1 + Royal2 + Zissou1 + Zissou1Continuous + Darjeeling1 + Darjeeling2 + Chevalier1 + FantasticFox1 + Moonrise1 + Moonrise2 + Moonrise3 + Cavalcanti1 + GrandBudapest1 + GrandBudapest2 + IsleofDogs1 + IsleofDogs2 + FrenchDispatch + AsteroidCity1 + AsteroidCity2 + AsteroidCity3 + end + + methods + function cmap = rgb(obj, n) + % Return palette as Nx3 RGB colormap [0–1] + + hex = obj.hex(); + + rgb = hex2rgb(hex); + + 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,:); + else + cmap = rgb; + end + end + end + + 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 +end diff --git a/Libs/wesanderson_colors/hex2rgb.m b/Libs/wesanderson_colors/hex2rgb.m new file mode 100644 index 0000000..b193e8d --- /dev/null +++ b/Libs/wesanderson_colors/hex2rgb.m @@ -0,0 +1,20 @@ +function rgb = hex2rgb(hex) +% HEX2RGB Convert HEX color codes to RGB [0–1] + +if ischar(hex) + hex = {hex}; +end + +n = numel(hex); +rgb = zeros(n,3); + +for k = 1:n + h = hex{k}; + h = strrep(h,'#',''); + rgb(k,1) = hex2dec(h(1:2)); + rgb(k,2) = hex2dec(h(3:4)); + rgb(k,3) = hex2dec(h(5:6)); +end + +rgb = rgb / 255; +end diff --git a/Libs/wesanderson_colors/minimal_example_wespalette.m b/Libs/wesanderson_colors/minimal_example_wespalette.m new file mode 100644 index 0000000..f55d2ad --- /dev/null +++ b/Libs/wesanderson_colors/minimal_example_wespalette.m @@ -0,0 +1,64 @@ +x = -10:2:25; % Input power [dBm] + +y1 = 1e-5 * 10.^(0.12*x); % Dispersion-only +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); +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); +plot(x,y2,'LineWidth',lw,'Color',cmap(2,:),'Marker','square','MarkerEdgeColor',cmap(2,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms); +plot(x,y3,'LineWidth',lw,'Color',cmap(3,:),'Marker','o','MarkerEdgeColor',cmap(3,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms); +plot(x,y4,'LineWidth',lw,'Color',cmap(4,:),'Marker','o','MarkerEdgeColor',cmap(4,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms); +yline(3.8e-3) + +grid on +xlabel('Input power [dBm]') +ylabel('NSD ($\%$)') +legend({'Dispersion','NLPN','RP','RP on $\beta_2$'}, ... + 'Location','best') + +grid off +set(gca,'MinorGridLineWidth',0.5); +set(gca,'GridLineWidth',0.5,'GridLineStyle','--','GridColor',[0.9,0.9,0.9]); + +set(gca,'FontSize',12,'YScale','log'); +ylim([1e-6 1e3]) +xlim([-10 23]) + +% % Create textarrow +% annotation(figure1,'textarrow',[0.564444444444444 0.548148148148148],... +% [0.768523809523809 0.63047619047619],'String',{'(A)'}); +% +% % Create doublearrow +% annotation(figure1,'doublearrow',[0.724444444444444 0.699259259259259],... +% [0.854238095238095 0.723809523809524]); +% +% % Create line +% annotation(figure1,'line',[0.700740740740741 0.699259259259259],... +% [0.554285714285714 0.405714285714286]); +% +% % Create textbox +% annotation(figure1,'textbox',... +% [0.578777777777778 0.194285714285714 0.104185185185185 0.0685714285714286],... +% 'String',{'BOX'},... +% 'FitBoxToText','off'); + +% +fig_path = 'C:\Users\Silas\Documents\Dissertation\00_Examples\tikz\textfig.tikz'; +matlab2tikz(fig_path, ... + '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=2',... + '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}',... + }); \ No newline at end of file diff --git a/Libs/wesanderson_colors/wes_palettes.m b/Libs/wesanderson_colors/wes_palettes.m new file mode 100644 index 0000000..df744b5 --- /dev/null +++ b/Libs/wesanderson_colors/wes_palettes.m @@ -0,0 +1,79 @@ +function palettes = wes_palettes() +% WES_PALETTES Full Wes Anderson color palette collection for MATLAB +% Colors are stored as HEX and converted to RGB on demand. + +palettes = struct(); + +palettes.BottleRocket1 = { ... + '#A42820', '#5F5647', '#9B110E', '#3F5151', '#4E2A1E', '#550307', '#0C1707'}; + +palettes.BottleRocket2 = { ... + '#FAD510', '#CB2314', '#273046', '#354823', '#1E1E1E'}; + +palettes.Rushmore1 = { ... + '#E1BD6D', '#EABE94', '#0B775E', '#35274A', '#F2300F'}; + +palettes.Rushmore = palettes.Rushmore1; + +palettes.Royal1 = { ... + '#899DA4', '#C93312', '#FAEFD1', '#DC863B'}; + +palettes.Royal2 = { ... + '#9A8822', '#F5CDB4', '#F8AFA8', '#FDDDA0', '#74A089'}; + +palettes.Zissou1 = { ... + '#3B9AB2', '#78B7C5', '#EBCC2A', '#E1AF00', '#F21A00'}; + +palettes.Zissou1Continuous = { ... + '#3A9AB2', '#6FB2C1', '#91BAB6', '#A5C2A3', '#BDC881', ... + '#DCCB4E', '#E3B710', '#E79805', '#EC7A05', '#EF5703', '#F11B00'}; + +palettes.Darjeeling1 = { ... + '#FF0000', '#00A08A', '#F2AD00', '#F98400', '#5BBCD6'}; + +palettes.Darjeeling2 = { ... + '#ECCBAE', '#046C9A', '#D69C4E', '#ABDDDE', '#000000'}; + +palettes.Chevalier1 = { ... + '#446455', '#FDD262', '#D3DDDC', '#C7B19C'}; + +palettes.FantasticFox1 = { ... + '#DD8D29', '#E2D200', '#46ACC8', '#E58601', '#B40F20'}; + +palettes.Moonrise1 = { ... + '#F3DF6C', '#CEAB07', '#D5D5D3', '#24281A'}; + +palettes.Moonrise2 = { ... + '#798E87', '#C27D38', '#CCC591', '#29211F'}; + +palettes.Moonrise3 = { ... + '#85D4E3', '#F4B5BD', '#9C964A', '#CDC08C', '#FAD77B'}; + +palettes.Cavalcanti1 = { ... + '#D8B70A', '#02401B', '#A2A475', '#81A88D', '#972D15'}; + +palettes.GrandBudapest1 = { ... + '#F1BB7B', '#FD6467', '#5B1A18', '#D67236'}; + +palettes.GrandBudapest2 = { ... + '#E6A0C4', '#C6CDF7', '#D8A499', '#7294D4'}; + +palettes.IsleofDogs1 = { ... + '#9986A5', '#79402E', '#CCBA72', '#0F0D0E', '#D9D0D3', '#8D8680'}; + +palettes.IsleofDogs2 = { ... + '#EAD3BF', '#AA9486', '#B6854D', '#39312F', '#1C1718'}; + +palettes.FrenchDispatch = { ... + '#90D4CC', '#BD3027', '#B0AFA2', '#7FC0C6', '#9D9C85'}; + +palettes.AsteroidCity1 = { ... + '#0A9F9D', '#CEB175', '#E54E21', '#6C8645', '#C18748'}; + +palettes.AsteroidCity2 = { ... + '#C52E19', '#AC9765', '#54D8B1', '#B67C3B', '#175149', '#AF4E24'}; + +palettes.AsteroidCity3 = { ... + '#FBA72A', '#D3D4D8', '#CB7A5C', '#5785C1'}; + +end diff --git a/projects.mat b/projects.mat new file mode 100644 index 0000000..8880e51 Binary files /dev/null and b/projects.mat differ diff --git a/projects/ECOC_2025/dsp_run_id.m b/projects/ECOC_2025/dsp_run_id.m index d1941a9..ae4280b 100644 --- a/projects/ECOC_2025/dsp_run_id.m +++ b/projects/ECOC_2025/dsp_run_id.m @@ -243,7 +243,6 @@ for occ = 1:record_realizations fprintf("BER DB: %.2e \n",dbenc_package{occ}.resultsDBsignaling.BER); - end diff --git a/projects/ECOC_2025/ecoc2025.db b/projects/ECOC_2025/ecoc2025.db new file mode 100644 index 0000000..e28ebc8 Binary files /dev/null and b/projects/ECOC_2025/ecoc2025.db differ diff --git a/projects/ECOC_2025/ecoc2025.sqbpro b/projects/ECOC_2025/ecoc2025.sqbpro new file mode 100644 index 0000000..87c9a0c --- /dev/null +++ b/projects/ECOC_2025/ecoc2025.sqbpro @@ -0,0 +1 @@ +
diff --git a/projects/ECOC_2025/ecoc2025_einmessung.db b/projects/ECOC_2025/ecoc2025_einmessung.db new file mode 100644 index 0000000..f8e12f5 Binary files /dev/null and b/projects/ECOC_2025/ecoc2025_einmessung.db differ diff --git a/projects/ECOC_2025/ecoc2025_fail.db b/projects/ECOC_2025/ecoc2025_fail.db new file mode 100644 index 0000000..af35623 Binary files /dev/null and b/projects/ECOC_2025/ecoc2025_fail.db differ diff --git a/projects/ECOC_2025/ecoc2025_loops - Kopie.db b/projects/ECOC_2025/ecoc2025_loops - Kopie.db new file mode 100644 index 0000000..bb4d7cf Binary files /dev/null and b/projects/ECOC_2025/ecoc2025_loops - Kopie.db differ diff --git a/projects/ECOC_2025/ecoc2025_loops.db b/projects/ECOC_2025/ecoc2025_loops.db new file mode 100644 index 0000000..ae8f55d Binary files /dev/null and b/projects/ECOC_2025/ecoc2025_loops.db differ diff --git a/projects/ECOC_2025/load_signal_standalone.m b/projects/ECOC_2025/load_signal_standalone.m index f58c77f..e54816a 100644 --- a/projects/ECOC_2025/load_signal_standalone.m +++ b/projects/ECOC_2025/load_signal_standalone.m @@ -1,34 +1,20 @@ +db = DBHandler("type","mysql","dataBase",'labor'); + +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 987); +M = 4; +fp.where('Runs', 'pam_level','EQUALS', M); +fp.where('Runs', 'symbolrate','EQUALS', 112e9); +fp.where('Runs', 'fiber_length','EQUALS', 0); +fp.where('Runs', 'is_mpi','EQUALS', 1); +fp.where('Runs', 'interference_path_length','EQUALS', 70); +% fp.where('Runs', 'loop_id','GREATER_THAN', 11); +fp.where('Runs', 'sir','EQUALS',20); -savePath = 'Z:\2025\ECOC Silas\ecoc_2025\'; -databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; -database_name = 'ecoc2025_loops.db'; -db = DBHandler("type","mysql"); -% db = DBHandler("pathToDB", [databasePath, database_name],"type","sqlite"); -filterParams = db.tables; -% filterParams.Configurations = struct('run_id', run_id); -filterParams.Configurations = struct( ... - 'symbolrate', 112e9, ... %[224,336,360,390,420,448] - 'fiber_length', 0, ... - 'db_mode', '"no_db"', ... - 'interference_attenuation', 4, ... - 'interference_path_length', 300, ... - 'is_mpi', 1, ... - 'pam_level', 4, ... - 'wavelength', 1310, ... - 'precomp_amp', [], ... - 'signal_attenuation', [], ... - 'v_awg', [], ... - 'v_bias', [] ... - ); - -selectedFields = {'Runs.run_id','Runs.tx_bits_path','Runs.tx_signal_path','Runs.tx_symbols_path','Runs.rx_sync_path','Runs.rx_raw_path',... - 'Configurations.db_mode','Configurations.pam_level','Configurations.bitrate','Configurations.symbolrate','Configurations.fiber_length','Configurations.wavelength','Configurations.precomp_amp','Measurements.power_rop','Configurations.v_bias',... - 'Configurations.interference_attenuation', 'Measurements.power_mpi_interference'}; - -[dataTable,sql_query] = db.queryDB(filterParams, selectedFields); +[dataTable,sql_query] = db.queryDB(fp, db.getTableFieldNames('Runs')); [~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices @@ -55,7 +41,7 @@ for i = 1:size(dataTable,1) Scpe_sig_raw = load([savePath, char(dataTable_.rx_raw_path(1))]); Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; - % Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0); + Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0); Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",fsym); diff --git a/projects/ECOC_2025/measure_run_script.m b/projects/ECOC_2025/measure_run_script.m index 4abd8eb..4ab6337 100644 --- a/projects/ECOC_2025/measure_run_script.m +++ b/projects/ECOC_2025/measure_run_script.m @@ -8,14 +8,14 @@ end precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\"; precomp_fn = "precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias_shot2"; precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active -run_lab_automation = 1; +run_lab_automation = 0; %% Configuration conf = confRequest; referenceFields = fieldnames(db.tables.Configurations); -assert(isequal(fieldnames(conf), referenceFields), 'Fieldnames do not match the reference structure!'); +% assert(isequal(fieldnames(conf), referenceFields), 'Fieldnames do not match the reference structure!'); %% Lab Automation @@ -28,7 +28,6 @@ if run_lab_automation [v_bias_meas,i_bias_meas]=dcs.readVals(); end % Laser - if (any(confPrev.laser_power ~= conf.laser_power)) || (~exist('laser','var')) || (isempty(confPrev.laser_power)) laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib'); laser.setWavelength(conf.wavelength); @@ -42,24 +41,65 @@ if run_lab_automation end voa.set('value',[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation]); - if voa.power_state(3) > -24 || voa.power_state(1)+ voa.atten_state(4) > -38 + if voa.power_state(3) > -24 || voa.power_state(1)+ voa.atten_state(4) > -37 holdAndShowValue end - + % PDFA if ~exist('pdfa','var') pdfa = Thor_PDFA("safety_mode",0,"serialport_number",'COM15'); end - % Construct AWG and Scope Modules %%%%%% - fdac = 224e9; - fadc = 160e9; - Scp = ScopeKeysight("model","DSAZ634A",'autoscale',0,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",recordlen,"removeDC",1,"extRef",1); - Awg = AwgKeysight("model","M8199A_ILV","fdac",fdac,"scaletodac",[1,1],"skews",[0,0],"voltages",[0,conf.v_awg]); - A2S = Awg2Scope(Awg,Scp,[0,0,3,0],"waitUntilClick",0); - end +% if conf.symbolrate == 72e9 +% if conf.pam_level == 2 +% conf.v_awg = 0.55; +% conf.pulsef_alpha = 0.6; +% elseif conf.pam_level == 4 +% conf.v_awg = 0.55; +% conf.pulsef_alpha =0.6; +% elseif conf.pam_level == 6 +% conf.v_awg = 0.55; +% conf.pulsef_alpha = 0.6; +% elseif conf.pam_level == 8 +% conf.v_awg = 0.55; +% conf.pulsef_alpha = 0.6; +% end +% elseif conf.symbolrate == 96e9 +% if conf.pam_level == 2 +% conf.v_awg = 0.8; +% conf.pulsef_alpha = 0.2; +% elseif conf.pam_level == 4 +% conf.v_awg = 0.8; +% conf.pulsef_alpha = 0.2; +% elseif conf.pam_level == 6 +% conf.v_awg = 0.8; +% conf.pulsef_alpha = 0.2; +% elseif conf.pam_level == 8 +% conf.v_awg = 0.8; +% conf.pulsef_alpha = 0.2; +% end +% elseif conf.symbolrate == 112e9 +% if conf.pam_level == 2 +% conf.v_awg = 0.85; +% conf.pulsef_alpha = 0.2; +% elseif conf.pam_level == 4 +% conf.v_awg = 0.85; +% conf.pulsef_alpha = 0.2; +% elseif conf.pam_level == 6 +% conf.v_awg = 0.85; +% conf.pulsef_alpha = 0.2; +% elseif conf.pam_level == 8 +% conf.v_awg = 0.85; +% conf.pulsef_alpha = 0.2; +% end +% end + +awg_upload_required = 1; +if any(confPrev.pam_level == conf.pam_level) && any(confPrev.symbolrate == conf.symbolrate) + awg_upload_required = 0; +end %% Signal generation and transmission if awg_upload_required || isempty(loop_id) @@ -70,6 +110,13 @@ if awg_upload_required || isempty(loop_id) sequence_order = min(18,sequence_order); end + % Construct AWG and Scope Modules %%%%%% + fdac = 224e9; + fadc = 160e9; + Scp = ScopeKeysight("model","DSAZ634A",'autoscale',0,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",recordlen,"removeDC",1,"extRef",1); + Awg = AwgKeysight("model","M8199A_ILV","fdac",fdac,"scaletodac",[1,1],"skews",[0,0],"voltages",[0,conf.v_awg]); + A2S = Awg2Scope(Awg,Scp,[0,0,3,0],"waitUntilClick",0); + Pamsource = PAMsource(... "fsym",conf.symbolrate,"M",conf.pam_level,"order",sequence_order,"useprbs",1,... "fs_out",fdac,... @@ -80,11 +127,11 @@ if awg_upload_required || isempty(loop_id) "randkey",1,... "duobinary_mode", conf.db_mode); - conf.pam_source = Pamsource; - confPrev = conf; - [Digi_sig,Symbols,Bits] = Pamsource.process(); + figure(10);clf; + Digi_sig.spectrum("displayname",'Tx Pulse Shaped','fignum',10,'normalizeTo0dB',1); + %%%%% Precompensation Routine %%%%%% if precomp_mode == 1 % measure channel @@ -95,11 +142,13 @@ if awg_upload_required || isempty(loop_id) Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',conf.precomp_amp,'loadPath',precomp_path,'fileName',precomp_fn); end - % Digi_sig.spectrum("displayname",'Tx Spectrum','fignum',10,'normalizeTo0dB',0); + Digi_sig.spectrum("displayname",'Tx Pre-Emphasized','fignum',10,'normalizeTo0dB',1); %%%%% Resample to DAC rate %%%%%% Digi_sig = Digi_sig.resample("fs_out",Awg.fdac); + Digi_sig.spectrum("displayname",'Tx Resampled','fignum',10,'normalizeTo0dB',1); + % [~,~,Scpe_sig_raw,~] = A2S.process("signal2",Digi_sig); Awg.upload("signal2",Digi_sig); @@ -114,6 +163,8 @@ if precomp_mode == 1 return; % End routine if precomp mode is active end +confPrev = conf; + %% Save static TX data (only once) currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss'); timeStr = char(currentTime); @@ -143,7 +194,7 @@ for recIdx = 1:n_recording end % Plot spectrum and time domain (optional) - % Scpe_sig_raw.spectrum("displayname", 'Rx Spectrum', 'fignum', 10, 'normalizeTo0dB', 0); + Scpe_sig_raw.spectrum("displayname", 'Rx Spectrum', 'fignum', 10, 'normalizeTo0dB', 0); Scpe_sig_raw.plot("clear", 1, "displayname", 'Rx Signal', 'fignum', 11); %% Save Routine @@ -154,7 +205,6 @@ for recIdx = 1:n_recording rx_raw_path = [filesep, current_folder, filesep, filename, '_rx_signal_raw']; save([savePath, rx_raw_path], "Scpe_sig_raw"); - % Check if loop_id is already created if isempty(loop_id) newLoop = db.tables.LoopControl; @@ -166,7 +216,6 @@ for recIdx = 1:n_recording fprintf('LoopControl entry created: loop_id = %d\n', loop_id); end - %% Append to Runs Table newRun = db.tables.Runs; newRun.run_id = NaN; @@ -184,9 +233,12 @@ for recIdx = 1:n_recording %% Append to Configurations Table conf.configuration_id = NaN; conf.run_id = run_id; + conf.bitrate = conf.symbolrate .* floor(log2(6)*10)/10; + conf.pam_source = Pamsource; db.appendToTable('Configurations', conf); %% Append to Measurements Table + laser.getLaserInfo; meas = db.tables.Measurements; meas.measurement_id = NaN; meas.run_id = run_id; @@ -204,12 +256,17 @@ for recIdx = 1:n_recording db.appendToTable('Measurements', meas); - %% Submit DSP Routine - - if subimt_DSP - [out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ... - "parallel", parallel_dsp, "max_occurences", max_occurences); + if recIdx == 1 + if subimt_DSP + [out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ... + "parallel", parallel_dsp, "max_occurences", max_occurences); + end + if awg_upload_required + [out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ... + "parallel", 0, "max_occurences", 1); + end end + end diff --git a/projects/ECOC_2025/plots_from_database/bias_vs_ber.m b/projects/ECOC_2025/plots_from_database/bias_vs_ber.m index 60977a2..b92698e 100644 --- a/projects/ECOC_2025/plots_from_database/bias_vs_ber.m +++ b/projects/ECOC_2025/plots_from_database/bias_vs_ber.m @@ -2,7 +2,7 @@ basePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; database_name = 'ecoc2025.db'; -database = DBHandler("pathToDB", [basePath, database_name]); +database = DBHandler("pathToDB", [basePath, database_name],"type",'mysql'); filterParams = database.tables; filterParams.Configurations = struct( ... diff --git a/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m index 297f17b..276c7d2 100644 --- a/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m +++ b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m @@ -1,49 +1,58 @@ -% basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -% database = DBHandler("pathToDB",[basePath,'silas_labor.db']); -database = DBHandler("type",'mysql','dataBase','labor'); -filterParams = database.tables; -filterParams.Runs.loop_id = 209; -% filterParams.Configurations = struct( ... -% 'symbolrate', 112e9, ... %[224,336,360,390,420,448] -% 'fiber_length', 0, ... -% 'db_mode', '"no_db"', ... -% 'interference_attenuation', [], ... -% 'interference_path_length', 1000, ... -% 'is_mpi', 1, ... -% 'pam_level', 4, ... -% 'wavelength', 1310, ... -% 'precomp_amp', [], ... -% 'signal_attenuation', [], ... -% 'v_awg', [], ... -% 'v_bias', [] ... -% ); +% dsp_options.database_type = 'mysql'; +% dsp_options.dataBase = 'labor'; +% dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\'; +% database = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type); +% filterParams = database.tables; +% filterParams.Runs.loop_id = 209; +% % filterParams.Configurations = struct( ... +% % 'symbolrate', 112e9, ... %[224,336,360,390,420,448] +% % 'fiber_length', 0, ... +% % 'db_mode', '"no_db"', ... +% % 'interference_attenuation', [], ... +% % 'interference_path_length', 1000, ... +% % 'is_mpi', 1, ... +% % 'pam_level', 4, ... +% % 'wavelength', 1310, ... +% % 'precomp_amp', [], ... +% % 'signal_attenuation', [], ... +% % 'v_awg', [], ... +% % 'v_bias', [] ... +% % ); +% +% % if 1 +% % % filterParams.EqualizerParameters.dc_buffer_len = 1; +% % filterParams.EqualizerParameters.ffe_buffer_len = 1; +% % filterParams.EqualizerParameters.smoothing_buffer_len = 4096; +% % filterParams.EqualizerParameters.smoothing_buffer_update = 224; +% % filterParams.EqualizerParameters.DCmu = 0; +% % end +% a = database.getTableFieldNames('Runs'); +% b = database.getTableFieldNames('Results'); +% c = database.getTableFieldNames('EqualizerParameters'); +% d = [a;b;c]; +% +% [dataTable,~] = database.queryDB(filterParams, d); +% +% selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Runs.bitrate' 'Runs.v_bias' 'Runs.v_awg' 'Runs.precomp_amp' 'Runs.symbolrate' 'Runs.pam_level'... +% 'Runs.db_mode' 'Runs.rop_attenuation' 'Runs.is_mpi' 'Runs.interference_attenuation' 'Runs.interference_path_length' 'Runs.signal_attenuation' ... +% 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'EqualizerParameters.dc_buffer_len' 'EqualizerParameters.ffe_buffer_len' 'EqualizerParameters.smoothing_buffer_len' 'EqualizerParameters.smoothing_buffer_update' 'EqualizerParameters.DCmu' 'Measurements.power_pd_in' ... +% 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.EVM' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'}; -% if 1 -% % filterParams.EqualizerParameters.dc_buffer_len = 1; -% filterParams.EqualizerParameters.ffe_buffer_len = 1; -% filterParams.EqualizerParameters.smoothing_buffer_len = 4096; -% filterParams.EqualizerParameters.smoothing_buffer_update = 224; -% filterParams.EqualizerParameters.DCmu = 0; -% end -a = database.getTableFieldNames('Runs'); -b = database.getTableFieldNames('Results'); -c = database.getTableFieldNames('EqualizerParameters'); -d = [a;b;c]; +db = DBHandler("type","mysql","dataBase",'labor'); -[dataTable,~] = database.queryDB(filterParams, d); +fp = QueryFilter(); +% fp.where('mpi_superview', 'loop_id','EQUALS', 209); +fp.where('mpi_superview', 'symbolrate','EQUALS', 112e9); +fp.where('mpi_superview', 'pam_level','EQUALS', 4); +fn = [db.getTableFieldNames('mpi_superview')]; +[dataTable,sql_query] = db.queryDB(fp,fn); -selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Runs.bitrate' 'Runs.v_bias' 'Runs.v_awg' 'Runs.precomp_amp' 'Runs.symbolrate' 'Runs.pam_level'... - 'Runs.db_mode' 'Runs.rop_attenuation' 'Runs.is_mpi' 'Runs.interference_attenuation' 'Runs.interference_path_length' 'Runs.signal_attenuation' ... - 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'EqualizerParameters.dc_buffer_len' 'EqualizerParameters.ffe_buffer_len' 'EqualizerParameters.smoothing_buffer_len' 'EqualizerParameters.smoothing_buffer_update' 'EqualizerParameters.DCmu' 'Measurements.power_pd_in' ... - 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.EVM' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'}; - -[dataTable_raw,sql_query] = database.queryDB(filterParams, selectedFields); %% -dataTable_clean = dataTable_raw; +dataTable_clean = dataTable; dataTable_clean.SIR = -7 - round(dataTable_clean.power_mpi_interference); dataTable_clean.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level); dataTable_clean = cleanUpTable(dataTable_clean); @@ -66,7 +75,7 @@ for int_len = [0,50,300,1000] mode = 4; - for mode = [1,4] + for mode = [1,2] hold on; dataTable = dataTable_clean; @@ -124,7 +133,7 @@ for int_len = [0,50,300,1000] method = 'ideal dc tracking'; end - dataTable(dataTable.eq_id==0,:) = []; + % dataTable(dataTable.eq_id==0,:) = []; dataTable(dataTable.equalizer_structure~=1,:) = []; % Modify values in 'interference_path_length' where the condition is met @@ -182,6 +191,10 @@ for int_len = [0,50,300,1000] dataTableGrpd_min = groupIt(fixedVars, dataTable, @min); dataTableGrpd_max = groupIt(fixedVars, dataTable, @max); + % dataTableGrpd_mean(dataTableGrpd_mean.nRows<50,:) = []; + % dataTableGrpd_min(dataTableGrpd_min.nRows<50,:) = []; + % dataTableGrpd_max(dataTableGrpd_max.nRows<50,:) = []; + % Create a new figure hold on @@ -236,6 +249,8 @@ for int_len = [0,50,300,1000] % Hide patch (shaded area) from legend set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none'); + + % Fit a 4th-order polynomial to log10(BER) p = polyfit(x_values, log10(y_mean), 3); % 4 is fitting order, adjust as needed @@ -286,9 +301,10 @@ for int_len = [0,50,300,1000] 'LineWidth', 0.5, 'HandleVisibility', 'off', 'DisplayName', string(dispname)); pair_one = {'Run ID', dataTable.run_id(loopFiltSingle, :)}; - pair_two = {'Rate', dataTable.bitrate(loopFiltSingle, :) * 1e-9}; + pair_two = {'Baud', dataTable.symbolrate(loopFiltSingle, :) * 1e-9}; pair_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)}; - addDatatips(sc, pair_one, pair_two, pair_three); + pair_four = {'#bits', round(dataTable.numBits(loopFiltSingle, :), 2)}; + addDatatips(sc, pair_one, pair_two, pair_three,pair_four); end end diff --git a/projects/ECOC_2025/precomp_1km.mat b/projects/ECOC_2025/precomp_1km.mat new file mode 100644 index 0000000..5c4c625 Binary files /dev/null and b/projects/ECOC_2025/precomp_1km.mat differ diff --git a/projects/ECOC_2025/precomp_1km_1mm_cable.mat b/projects/ECOC_2025/precomp_1km_1mm_cable.mat new file mode 100644 index 0000000..43937a4 Binary files /dev/null and b/projects/ECOC_2025/precomp_1km_1mm_cable.mat differ diff --git a/projects/ECOC_2025/precomp_1km_1mm_cable_100ghz_pd.mat b/projects/ECOC_2025/precomp_1km_1mm_cable_100ghz_pd.mat new file mode 100644 index 0000000..ed37b29 Binary files /dev/null and b/projects/ECOC_2025/precomp_1km_1mm_cable_100ghz_pd.mat differ diff --git a/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd.mat b/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd.mat new file mode 100644 index 0000000..9560ab1 Binary files /dev/null and b/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd.mat differ diff --git a/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias.mat b/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias.mat new file mode 100644 index 0000000..5befc3c Binary files /dev/null and b/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias.mat differ diff --git a/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias_shot2.mat b/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias_shot2.mat new file mode 100644 index 0000000..918b1fb Binary files /dev/null and b/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias_shot2.mat differ diff --git a/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shot2.mat b/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shot2.mat new file mode 100644 index 0000000..37932d1 Binary files /dev/null and b/projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shot2.mat differ diff --git a/projects/ECOC_2025/sqlite_sequence.sql b/projects/ECOC_2025/sqlite_sequence.sql new file mode 100644 index 0000000..b9e5076 --- /dev/null +++ b/projects/ECOC_2025/sqlite_sequence.sql @@ -0,0 +1,163 @@ +BEGIN TRANSACTION; +CREATE TABLE IF NOT EXISTS "Configurations" ( + "configuration_id" INTEGER, + "run_id" INTEGER, + "unique_elab_id" TEXT, + "bitrate" REAL, + "symbolrate" REAL, + "pam_level" INTEGER, + "db_mode" TEXT, + "pulsef_alpha" INTEGER, + "v_bias" REAL, + "v_awg" REAL, + "precomp_amp" REAL, + "rop_attenuation" REAL, + "wavelength" REAL, + "laser_power" REAL, + "fiber_length" REAL, + "pd_in_desired" REAL, + "is_mpi" BIT, + "signal_attenuation" REAL, + "interference_path_length" REAL, + "interference_attenuation" REAL, + "pam_source" TEXT, + PRIMARY KEY("configuration_id" AUTOINCREMENT), + FOREIGN KEY("run_id") REFERENCES "Runs"("run_id") +); +CREATE TABLE IF NOT EXISTS "EqualizerParameters" ( + "eq_id" INTEGER, + "equalizer_structure" REAL, + "M" INTEGER, + "target_constellation" TEXT, + "db_target" INTEGER, + "diff_precode" INTEGER, + "postFFE" INTEGER, + "NpostFFE" INTEGER, + "Ne1" INTEGER, + "Ne2" INTEGER, + "Ne3" INTEGER, + "Nb1" INTEGER, + "Nb2" INTEGER, + "Nb3" INTEGER, + "K" INTEGER, + "DCmu" REAL, + "ideal_dfe" INTEGER, + "training_length" INTEGER, + "training_loops" INTEGER, + "TRmu1" REAL, + "TRmu2" REAL, + "TRmu3" REAL, + "TRmuDFE" REAL, + "dd_loops" INTEGER, + "DDmu1" REAL, + "DDmu2" REAL, + "DDmu3" REAL, + "DDmuDFE" REAL, + "MLSE_mode" TEXT, + "MLSE_trellis_states" TEXT, + "comment" TEXT, + "config_hash" TEXT, + UNIQUE("config_hash"), + PRIMARY KEY("eq_id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "Measurements" ( + "measurement_id" INTEGER, + "run_id" INTEGER, + "power_laser" REAL, + "power_rop" REAL, + "power_pd_in" REAL, + "power_mpi_interference" REAL, + "power_mpi_signal" REAL, + "voa_class" TEXT, + "pdfa_class" TEXT, + "laser_class" TEXT, + PRIMARY KEY("measurement_id" AUTOINCREMENT), + FOREIGN KEY("run_id") REFERENCES "Runs"("run_id") +); +CREATE TABLE IF NOT EXISTS "Results" ( + "result_id" INTEGER, + "run_id" INTEGER, + "eqParam_id" INTEGER, + "date_of_processing" DATETIME DEFAULT (datetime('now', 'localtime')), + "numBits" INTEGER, + "numBitErr" INTEGER, + "BER" REAL, + "numBitErr_precoded" REAL, + "BER_precoded" REAL, + "SNR" REAL, + "SNR_level" TEXT, + "GMI" REAL, + "AIR" REAL, + "EVM" REAL, + "EVM_level" TEXT, + "Alpha" REAL, + "result_hash" TEXT UNIQUE, + "MLSE_dir" INTEGER, + PRIMARY KEY("result_id" AUTOINCREMENT), + FOREIGN KEY("eqParam_id") REFERENCES "EqualizerParameters"("eq_id"), + FOREIGN KEY("run_id") REFERENCES "Runs"("run_id") +); +CREATE TABLE IF NOT EXISTS "Runs" ( + "run_id" INTEGER, + "date_of_run" DATETIME DEFAULT (datetime('now', 'localtime')), + "tx_bits_path" TEXT, + "tx_symbols_path" TEXT, + "rx_sync_path" TEXT, + "rx_raw_path" TEXT, + "filename" TEXT, + "tx_signal_path" TEXT, + PRIMARY KEY("run_id" AUTOINCREMENT) +); +CREATE VIEW "View_ResultOverview" AS +SELECT + -- Run info + Runs.run_id, + Runs.date_of_run, + + Results.BER, + Results.SNR, + Results.GMI, + Results.AIR, + Results.EVM, + Results.Alpha, + + -- Configurations + Configurations.symbolrate, + Configurations.pam_level, + Configurations.db_mode, + Configurations.pulsef_alpha, + Configurations.v_bias, + Configurations.v_awg, + Configurations.precomp_amp, + Configurations.is_mpi, + Configurations.signal_attenuation, + Configurations.interference_path_length, + Configurations.interference_attenuation, + + -- Measurement data + Measurements.power_laser, + Measurements.power_rop, + Measurements.power_pd_in, + Measurements.power_mpi_interference, + Measurements.power_mpi_signal, + + -- Equalizer parameters + EqualizerParameters.equalizer_structure, + EqualizerParameters.db_target, + EqualizerParameters.diff_precode + + +FROM Results + +-- Join related tables +LEFT JOIN Runs ON Results.run_id = Runs.run_id +LEFT JOIN Configurations ON Configurations.run_id = Runs.run_id +LEFT JOIN Measurements ON Measurements.run_id = Runs.run_id +LEFT JOIN EqualizerParameters ON Results.eqParam_id = EqualizerParameters.eq_id; +CREATE INDEX IF NOT EXISTS "idx_run_id_on_Configurations" ON "Configurations" ( + "run_id" +); +CREATE INDEX IF NOT EXISTS "idx_run_id_on_Measurements" ON "Measurements" ( + "run_id" +); +COMMIT; diff --git a/projects/ECOC_2025/submit_dsp.m b/projects/ECOC_2025/submit_dsp.m index 183ce2b..4c78fd2 100644 --- a/projects/ECOC_2025/submit_dsp.m +++ b/projects/ECOC_2025/submit_dsp.m @@ -19,7 +19,7 @@ if options.parallel % Check if a pool exists pool = gcp('nocreate'); if isempty(pool) - parpool; + parpool(4); end % Submit the DSP function asynchronously diff --git a/projects/ECOC_2025/theory/analytic_mpi_evaluation.m b/projects/ECOC_2025/theory/analytic_mpi_evaluation.m index 9df4f5b..460f24f 100644 --- a/projects/ECOC_2025/theory/analytic_mpi_evaluation.m +++ b/projects/ECOC_2025/theory/analytic_mpi_evaluation.m @@ -1,13 +1,13 @@ % This script is used to evaluate Fig. 1b) in the paper "Adaptive Removal of Multipath Interference in Short Reach 112 GBd PAM-4 IM/DD Systems" %% Parameters -df = 1e6;%150e3; % Laser linewidth [Hz] +df = 1e6; % Laser linewidth [Hz] SIR_dB = 20; % Interference attenuation [dB] alpha = 10^(-SIR_dB/20); % Interference attenuation [linear] n_fiber = 1.467; % Refractive index c = physconst('lightspeed'); % [m/s] -L = linspace(0,400,40); % Interference delay [m] +L = linspace(0,250,50); % Interference delay [m] tau = n_fiber./c.*L; % Interference time (= tau) [s] tau_c = 1/(pi*df); % laser coherence time [s] @@ -22,7 +22,7 @@ N = round(Tsim*fs); % number of samples for each realization max_delay_samples = round(max(tau)*fs); % largest delay that is evaluated (based on max. Interference delay) phase_noise_std = sqrt(2*pi*df/fs); % standard dev. phase noise -num_realizations = 10; % number of parallel runs +num_realizations = 50; % number of parallel runs monte_carlo_variance = zeros(num_realizations, length(L)); parfor r = 1:num_realizations @@ -48,7 +48,9 @@ avg_of_mc_variances = mean(monte_carlo_variance, 1); std_of_mc_variances = std(monte_carlo_variance, 0, 1); %% Analytic variance -analytic_variance = 2*alpha^2 * (1 - exp(-2*pi*df.*tau)).^2; +L_ = linspace(0,250,500); % Interference delay [m] +tau_ = n_fiber./c.*L_; +analytic_variance = 2*alpha^2 * (1 - exp(-2*pi*df.*tau_)).^2; %% Plot cols = [0.3467 0.5360 0.6907 @@ -62,20 +64,21 @@ hold on; plot(L, avg_of_mc_variances, 'LineWidth',2, 'DisplayName','Simulation','Color',cols(1,:),'LineStyle','-'); errorbar(L, avg_of_mc_variances,std_of_mc_variances, 'LineWidth',0.7,'LineStyle','none', 'DisplayName','Simulation','Color',cols(1,:),'HandleVisibility','off'); -plot(L, analytic_variance, 'LineWidth',2, 'DisplayName','Analytic','Color',cols(2,:),'LineStyle','-'); +plot(L_, analytic_variance, 'LineWidth',2, 'DisplayName','Analytic','Color',cols(2,:),'LineStyle','-'); xticks(coherence_length_multiples.*L_c); -xticklabels(round(coherence_length_multiples.*L_c)); +xticklabels(round(coherence_length_multiples.*L_c,1)); norm_to_coherence_len = 1; if norm_to_coherence_len xticklabels(coherence_length_multiples); - xlabel('$\tau_c$', 'FontSize',12); + xlabel('$n \cdot L_c$', 'FontSize',12); +else + xlabel('Interference Delay [m]', 'FontSize',12); end xline(L_c.*coherence_length_multiples, 'LineWidth',1.5, 'DisplayName','Coh. Length','HandleVisibility','off','Color',[0.7,0.7,0.7],'LineStyle','-'); xlim([0,L(end)]); yline(var_sat, '-.k','LineWidth',1.5, 'DisplayName','Saturation: 2$\alpha ^2$'); -xlabel('Interference Delay [m]', 'FontSize',12); grid on; ylabel('Intensity Variance', 'FontSize',12); title(sprintf('MPI Variance; %d MHz; SIR: %d dB',df.*1e-6,SIR_dB), 'FontSize',14); diff --git a/projects/ECOC_2025/theory/coherence_length_plot.m b/projects/ECOC_2025/theory/coherence_length_plot.m index e6d040d..b793687 100644 --- a/projects/ECOC_2025/theory/coherence_length_plot.m +++ b/projects/ECOC_2025/theory/coherence_length_plot.m @@ -1,32 +1,32 @@ %% Parameters -df = linspace(100e3,50e6,10000); % Laser FWHM linewidth [Hz] +df = linspace(1,50e6,10000); % Laser FWHM linewidth [Hz] n_fiber = 1.467; % Fiber group index c = 3e8; % Speed of light [m/s] % Compute coherence length (1/e of mean-fringe decay) tau_c = 1./(pi*df); -L_c = (c/n_fiber) .* tau_c; % Coherence length [m] +L_c = (c.* tau_c/n_fiber) ; % Coherence length [m] %% Plot figure('Color','w'); loglog(df/1e6, L_c, 'LineWidth',2,'LineStyle','-'); % linewidth in MHz -xticks([0.1, 1, 10, 50]); -yticks([1, 10, 100, 1000]); -yticklabels({'1','10','100','1000'}) +% xticks([0.1, 1, 10, 50]); +% yticks([1, 10, 100, 1000]); +% yticklabels({'1','10','100','1000'}) grid on; box on; -xlabel('Laser linewidth [MHz]','FontSize',12,'Interpreter','none'); -ylabel('Coherence length [m]','FontSize',12,'Interpreter','none'); -title('Coherence Length vs. Laser Linewidth','FontSize',14,'Interpreter','none'); +xlabel('Laser linewidth [MHz]','FontSize',12,'Interpreter','latex'); +ylabel('Coherence length [m]','FontSize',12,'Interpreter','latex'); +title('Coherence Length vs. Laser Linewidth','FontSize',14,'Interpreter','latex'); %% Annotate some key points -hold on; -freqs = [150e3, 1e6, 10e6, 50e6]; % [Hz] -for f = freqs - x = f/1e6; - y = (c/n_fiber) * (1/(pi*f)); - scatter(x,y,'Marker','x','LineWidth',1,'MarkerEdgeColor','black'); - - text(x*1.1,y, sprintf('%.2f MHz', f/1e6), ... - 'FontSize',10,'HorizontalAlignment','left'); - -end +% hold on; +% freqs = [150e3, 1e6, 10e6, 50e6]; % [Hz] +% for f = freqs +% x = f/1e6; +% y = (c/n_fiber) * (1/(pi*f)); +% scatter(x,y,'Marker','x','LineWidth',1,'MarkerEdgeColor','black'); +% +% text(x*1.1,y, sprintf('%.2f MHz', f/1e6), ... +% 'FontSize',10,'HorizontalAlignment','left'); +% +% end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_dispersion.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_dispersion.m index 447daac..9c0d120 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_dispersion.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_dispersion.m @@ -20,7 +20,7 @@ fp.where('Runs', 'rop_attenuation','EQUALS', 0); fields = db.getTableFieldNames('power_state_info'); -fields = [fields; db.getTableFieldNames('dashboard_ungrouped')]; +fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')]; [dataTable,~] = db.queryDB(fp, fields); eqstructures = unique(dataTable.equalizer_structure); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m index d39a24c..38ff1cf 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m @@ -5,7 +5,7 @@ db = DBHandler("dataBase", [dataBase], "type", database_type); fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); -M = 8; +M = 6; fp.where('Runs', 'pam_level','EQUALS', M); % fp.where('Runs', 'bitrate','LESS_THAN', 310e9); fp.where('Runs', 'fiber_length','EQUALS', 2); @@ -13,12 +13,12 @@ fp.where('Runs', 'is_mpi','EQUALS', 0); % fp.where('Runs', 'interference_path_length','EQUALS', 1000); % fp.where('Runs', 'loop_id','GREATER_THAN', 11); % fp.where('Runs', 'sir','EQUALS',18); -fp.where('Runs', 'wavelength','EQUALS', 1293); +fp.where('Runs', 'wavelength','EQUALS', 1310); % fp.where('Runs', 'db_mode','EQUALS', 0); fp.where('Runs', 'rop_attenuation','EQUALS', 0); fields = db.getTableFieldNames('power_state_info'); -fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')]; +fields = [fields; db.getTableFieldNames('dashboard_ungrouped_aug_nov_2025')]; [dataTable,~] = db.queryDB(fp, fields); eqstructures = unique(dataTable.equalizer_structure); @@ -29,7 +29,7 @@ show_bitrate = true; figure(5); hold on -for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse] +for eqs = [equalizer_structure.vnle] % figure('Name',string([char(eqs),''])); % hold on @@ -148,13 +148,14 @@ for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse] xticks(xraw); if show_bitrate - xticks(200:25:500); - xlim([350 500]); + % xticks(200:25:500); + % xlim([350 500]); + xlim([min(xraw), max(xraw)]); else xlim([min(xraw), max(xraw)]); end - ylim([5e-4, 0.3]); + ylim([1e-4, 0.5]); end yline([2.2e-4, 4.85e-3, 2e-2],'LineWidth',1,'LineStyle','--','HandleVisibility','off'); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate_only_best_configs.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate_only_best_configs.m index 58ef10d..e9a886e 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate_only_best_configs.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate_only_best_configs.m @@ -17,7 +17,7 @@ fp.where('Runs', 'wavelength','EQUALS', 1310); % fp.where('Runs', 'db_mode','EQUALS', 1); fp.where('Runs', 'rop_attenuation','EQUALS', 0); -[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard')); +[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard_ungrouped_after_nov_2025')); eqstructures = unique(dataTable.equalizer_structure); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_ROP.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_ROP.m new file mode 100644 index 0000000..d42b76d --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_ROP.m @@ -0,0 +1,146 @@ +%% ============================================================ +% SETTINGS +% ============================================================ +database_type = 'mysql'; +db = DBHandler("dataBase", "labor_highspeed", "type", database_type); + +fiberL = 1; % km +wlen = 1310; % nm +bit = 300e9; % example (adjust if needed) +max_pd = 7; % ROP limit (same as before) + +PAM_list = [4 6 8]; % formats to compare + +% Colors for PAM formats +colors = {clr.Paired.red, clr.Paired.green, clr.Paired.blue}; + +% Best DSP selection: +bestDSP = struct; +bestDSP = struct; +bestDSP.P4 = equalizer_structure.vnle_db_mlse; % PAM-4 +bestDSP.P6 = equalizer_structure.vnle; % PAM-6 +bestDSP.P8 = equalizer_structure.vnle; % PAM-8 + + + +%% ============================================================ +% LOOP over PAM formats — extract data +% ============================================================ +results = struct; + +for pi = 1:numel(PAM_list) + M = PAM_list(pi); + eq = bestDSP.(sprintf('P%d', M)); + + % ---- DB FILTER ---- + fp = QueryFilter(); + fp.where('Runs','pam_level','EQUALS',M); + fp.where('Runs','fiber_length','EQUALS',fiberL); + fp.where('Runs','wavelength','EQUALS',wlen); + fp.where('Runs','bitrate','EQUALS',bit); + fp.where('Runs','power_pd_in','LESS_THAN',max_pd); + + fields = [ + db.getTableFieldNames('power_state_info'); + db.getTableFieldNames('dashboard_ungrouped_alltime') + ]; + [T,~] = db.queryDB(fp, fields); + + % ---- DSP OPTIONS ---- + pre_emph = decide_preemph(M, eq); + precoded = decide_precoded(M, eq); + + cfg = struct; + cfg.x_axis = 'power_mzm'; + cfg.y_axis = 'BER'; + cfg.agg = 'min'; + cfg.outlier = 'none'; + cfg.show_raw = false; + + cfg.filters = struct( ... + 'pam_level', M, ... + 'fiber_length', fiberL, ... + 'wavelength', wlen, ... + 'bitrate', bit, ... + 'is_mpi', 0, ... + 'equalizer_structure', eq, ... + 'pre_emph', pre_emph); + + A = analyze_measurements_gpt(T, cfg); + + results(pi).M = M; + results(pi).x = A.group{1}.x; + results(pi).color = colors{pi}; + + if precoded + results(pi).ber = A.group{1}.y_precoded; + else + results(pi).ber = A.group{1}.y; + end +end + + +%% ============================================================ +% PLOT — all PAM formats in one ROP plot +% ============================================================ +fig = figure(91); hold on; + +lw = 2.2; ms = 7; + +for pi = 1:numel(results) + plot(results(pi).x, results(pi).ber, ... + '-o', ... + 'LineWidth', lw, ... + 'MarkerSize', ms, ... + 'MarkerFaceColor', results(pi).color, ... + 'Color', results(pi).color, ... + 'DisplayName', sprintf('PAM-%d', results(pi).M)); +end + +set(gca,'YScale','log'); +grid minor; + +xlabel('ROP / Power (MZM) [dBm]'); +ylabel('BER'); + +ylim([1e-4 2e-1]); + +legend('Location','best'); +title(sprintf('BER vs ROP — Best DSP (4,6,8) at %.0f GBd, λ=%d nm, %.0f km', ... + bit*1e-9, wlen, fiberL)); + +beautifyBERplot(); + +set(fig,'Position',1e3*[0.35 0.45 1.0 0.45]); + +%% ============================================================ +% DECISION LOGIC (INLINE FUNCTIONS) +% ============================================================ + +function pe = decide_preemph(M, eq) + % PRE-EMPH RULES: + switch M + case 4 + if eq == equalizer_structure.vnle + pe = 1; % PAM4: VNLE → pre-emph on + else + pe = 0; % PAM4: all others → off + end + case {6,8} + pe = 1; % PAM6/8: all → pre-emph on + otherwise + pe = 0; + end +end + + +function flag = decide_precoded(M, eq) + % PRE-CODE RULES: + if eq == equalizer_structure.vnle_db_mlse + flag = 1; % Always for DB-target + elseif eq == equalizer_structure.ml_mlse && M == 4 + flag = 1; % PAM4: ML-based → precoded + else + flag = 0; + end +end \ 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 new file mode 100644 index 0000000..21f881f --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m @@ -0,0 +1,272 @@ +%% ============================================================ +% LOAD DATA FOR PAM = 4,6,8 +% ============================================================ +database_type = 'mysql'; +db = DBHandler("dataBase", "labor_highspeed", "type", database_type); + +pam_levels = [4, 6, 8]; % three tiles +bitrate_set = 360e9; +fiberL = 10; + +fields = [ + db.getTableFieldNames('power_state_info'); + db.getTableFieldNames('dashboard_ungrouped_alltime') +]; + +%% ============================================================ +% DEFINE DSP SCHEMES +% ============================================================ +curves = struct; + +curves(1).name = 'VNLE'; +curves(1).eq = equalizer_structure.vnle; +curves(1).color = clr.Paired.red; + +curves(2).name = 'PF + MLSE'; +curves(2).eq = equalizer_structure.vnle_pf_mlse; +curves(2).color = clr.Paired.green; + +curves(3).name = 'DB-target + MLSE'; +curves(3).eq = equalizer_structure.vnle_db_mlse; +curves(3).color = clr.Paired.blue; + +curves(4).name = 'ML-based MLSE'; +curves(4).eq = equalizer_structure.ml_mlse; +curves(4).color = clr.Paired.purple; + +%% ============================================================ +% ANALYSIS — NO PLOTTING +% results(p, k) → p: PAM index, k: DSP index +% ============================================================ +results = struct; + +for p = 1:length(pam_levels) + M = pam_levels(p); + + % --- Load DB rows for this PAM --- + fp = QueryFilter(); + fp.where('Runs','pam_level','EQUALS', M); + fp.where('Runs','fiber_length','EQUALS', fiberL); + fp.where('Runs','bitrate','EQUALS', bitrate_set); + fp.where('Runs','is_mpi','EQUALS', 0); + + [dataTable, ~] = db.queryDB(fp, fields); + + for k = 1:numel(curves) + + %% ===================================================== + % DECIDE PRE-EMPHASIS AND PRECoded BER + % ====================================================== + pre_emph = decide_preemph(M, curves(k).eq); + use_precoded = decide_precoded(M, curves(k).eq); + + %% ---- base config ---- + cfg = struct; + cfg.x_axis = 'wavelength'; + cfg.y_axis = 'BER'; + cfg.agg = 'min'; + cfg.outlier = 'none'; + % cfg.group_by = {'wavelength'}; + cfg.show_raw = false; + + cfg.filters = struct( ... + 'pam_level', M, ... + 'is_mpi', 0, ... + 'bitrate', bitrate_set, ... + 'fiber_length', fiberL, ... + 'equalizer_structure', curves(k).eq, ... + 'pre_emph', pre_emph); + + %% ---- Run analysis ---- + A = analyze_measurements_gpt(dataTable, cfg); + + results(p,k).wavelength = A.group{1}.x; + + %% ---- store BER variant ---- + if use_precoded + results(p,k).ber = A.group{1}.y_precoded; + else + results(p,k).ber = A.group{1}.y; + end + + end +end + + +%% ============================================================ +% PLOT — 1×3 (PAM-4, PAM-6, PAM-8) +% ============================================================ +fig = figure(9110); clf; +tiledlayout(1,3,'TileSpacing','compact','Padding','compact'); + +lw = 1.8; +ms = 6; + +for p = 1:length(pam_levels) + nexttile; hold on; + + for k = 1:numel(curves) + plot(results(p,k).wavelength, results(p,k).ber, ... + '-o', ... + 'Color', curves(k).color, ... + 'MarkerFaceColor', curves(k).color, ... + 'MarkerSize', ms, ... + 'LineWidth', lw, ... + 'DisplayName', curves(k).name); + end + + set(gca,'YScale','log'); + grid on; + if p == 1 + ylabel('BER'); + else +ylabel(''); + end + xlabel('wavelength'); + + ylim([4e-4, 0.1]); + + beautifyBERplot(); + + yline([2.2e-4 4.85e-3 2e-2], ... + 'LineWidth',1.1, 'Color',[0.2 0.2 0.2], ... + 'LineStyle',':','HandleVisibility','off'); + + + if p == 1 + + x1 = 1290; + x2 = 1297; + x3 = 1300; + x4 = 1323; + x5 = 1325; + x6 = 1330; + + elseif p == 2 + + x1 = 1290; + x2 = 1295; + x3 = 1300; + x4 = 1323.5; + x5 = 1325; + x6 = 1330; + + elseif p == 3 + + x1 = 1290; + x2 = 1292; + x3 = 1298; + x4 = 1323; + x5 = 1327.5; + x6 = 1330; + end + + % --- Get current y-limits --- + yl = ylim; + + % --- LEFT AREA BELOW KP4 FEC --- + patch([x1 x2 x2 x1], [yl(1) yl(1) yl(2) yl(2)], ... + clr.Set1.red, ... % RGB = red + 'FaceAlpha', 0.1, ... % transparency 0.1 + 'EdgeColor', 'none'); % no border + + % --- RIGHT AREA BELOW KP4 FEC --- + patch([x2 x3 x3 x2], [yl(1) yl(1) yl(2) yl(2)], ... + clr.Set1.blue, ... % RGB = red + 'FaceAlpha', 0.10, ... % transparency 0.1 + 'EdgeColor', 'none'); % no border + + % --- LEFT AREA BELOW O-FEC --- + patch([x4 x5 x5 x4], [yl(1) yl(1) yl(2) yl(2)], ... + clr.Set1.blue, ... % RGB = red + 'FaceAlpha', 0.10, ... % transparency 0.1 + 'EdgeColor', 'none'); % no border + + % --- RIGHT AREA BELOW O-FEC --- + patch([x5 x6 x6 x5], [yl(1) yl(1) yl(2) yl(2)], ... + clr.Set1.red, ... % RGB = red + 'FaceAlpha', 0.10, ... % transparency 0.1 + 'EdgeColor', 'none'); % no border + + uistack(findobj(gca,'Type','patch'),'bottom'); % send the patch behind curves + + +% ax = gca; +% axpos = ax.Position; % [x y w h] normalized +% xl = xlim; +% yl = ylim; +% +% % Convert axis coords → normalized figure coords +% toNorm = @(x,y) [ ... +% axpos(1) + (x - xl(1)) / (xl(2)-xl(1)) * axpos(3), ... +% axpos(2) + (y - yl(1)) / (yl(2)-yl(1)) * axpos(4) ... +% ]; +% +% % Choose vertical placement (10% above bottom of axis) +% y_arrow = yl(1) * (yl(2)/yl(1))^0.10; % works with log-scale axes +% +% % === Arrow 1: x3 <-> x4 ====================================== +% p1 = toNorm(x3, y_arrow); +% p2 = toNorm(x4, y_arrow); +% +% annotation('doublearrow', ... +% [p1(1) p2(1)], [p1(2) p2(2)], ... +% 'Color', [0 0 0], 'LineWidth', 1.4); +% +% % === Arrow 2: x2 <-> x5 ====================================== +% p3 = toNorm(x2, y_arrow); +% p4 = toNorm(x5, y_arrow); +% +% annotation('doublearrow', ... +% [p3(1) p4(1)], [p3(2) p4(2)], ... +% 'Color', [0 0 0], 'LineWidth', 1.4); + +end + +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' ... + }); + + + +%% ============================================================ +% DECISION LOGIC (INLINE FUNCTIONS) +% ============================================================ + +function pe = decide_preemph(M, eq) + % PRE-EMPH RULES: + switch M + case 4 + if eq == equalizer_structure.vnle + pe = 1; % PAM4: VNLE → pre-emph on + else + pe = 0; % PAM4: all others → off + end + case {6,8} + pe = 1; % PAM6/8: all → pre-emph on + otherwise + pe = 0; + end +end + + +function flag = decide_precoded(M, eq) + % PRE-CODE RULES: + if eq == equalizer_structure.vnle_db_mlse + flag = 1; % Always for DB-target + elseif eq == equalizer_structure.ml_mlse && M == 4 + flag = 1; % PAM4: ML-based → precoded + else + flag = 0; + end +end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_BER_3x4.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_BER_3x4.m new file mode 100644 index 0000000..5bd84b7 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_BER_3x4.m @@ -0,0 +1,132 @@ +database_type = 'mysql'; +dataBase = 'labor_highspeed'; +db = DBHandler("dataBase", dataBase, "type", database_type); + +%% FILTER QUERY +fp = QueryFilter(); +fp.where('Runs', 'fiber_length','EQUALS', 2); +fp.where('Runs', 'wavelength','EQUALS', 1310); +fp.where('Runs', 'rop_attenuation','EQUALS', 0); + +fields = db.getTableFieldNames('power_state_info'); +fields = [fields; db.getTableFieldNames('dashboard_ungrouped_alltime')]; + +[dataTable,~] = db.queryDB(fp, fields); + +%% ---- CONFIG ---- +cfg = struct; +cfg.x_axis = 'grossrate'; +cfg.y_axis = 'BER'; +cfg.y_scale = 'log'; +cfg.outlier = 'mad'; +cfg.show_raw = false; +cfg.show_spread = 'none'; +cfg.agg = 'min'; +cfg.show_precoded = 1; +cfg.fec_lines = []; + +cfg.plot = struct; +cfg.plot.use_cbrewer2 = false; +cfg.plot.lineWidth = 2.0; +cfg.plot.errWidth = 1.2; +cfg.plot.scatterAlpha = 0.35; +cfg.plot.legendLocation = 'best'; +cfg.plot.fecLineWidth = 2.4; +cfg.plot.custom_colors_scatter = []; % disabled + +%% ---- DSP DEFINITIONS ---- +DSP(1).name = 'VNLE'; +DSP(1).eq = equalizer_structure.vnle; +DSP(1).color = clr.Paired.red; +DSP(1).lightcolor = clr.Paired.lightred; + +DSP(2).name = 'VNLE PF MLSE'; +DSP(2).eq = equalizer_structure.vnle_pf_mlse; +DSP(2).color = clr.Paired.green; +DSP(2).lightcolor = clr.Paired.lightgreen; + +DSP(3).name = 'VNLE DB MLSE'; +DSP(3).eq = equalizer_structure.vnle_db_mlse; +DSP(3).color = clr.Paired.blue; +DSP(3).lightcolor = clr.Paired.lightblue; + +DSP(4).name = 'ML MLSE'; +DSP(4).eq = equalizer_structure.ml_mlse; +DSP(4).color = clr.Paired.purple; +DSP(4).lightcolor = clr.Paired.lightpurple; + +%% ---- GRID CONFIG ---- +rows = 3; % PAM 4,6,8 +cols = 4; % DSP schemes +pam = [4 6 8]; + +cfg.figure_number = 46; +fig = figure(cfg.figure_number); clf; + +t = tiledlayout(rows, cols, ... + 'TileSpacing','compact', ... + 'Padding','compact'); + +cfg.group_by = {'equalizer_structure','pre_emph'}; +cfg.plot.use_cbrewer2 = false; + +%% ==== MAIN PLOT LOOP ===== +for r = 1:rows + Mlev = pam(r); + + for c = 1:cols + ax = nexttile(t, (r-1)*cols + c); + cfg.ax = ax; + + % ---- PRE-EMPH = 1 ---- + cfg.filters = struct('is_mpi',0,'pam_level',Mlev, ... + 'equalizer_structure',DSP(c).eq, ... + 'pre_emph',1); + cfg.plot.custom_colors = DSP(c).lightcolor; + cfg.plot.custom_linetypes = {'-'}; + [~, M1] = plot_measurements_gpt(dataTable, cfg); + + % ---- PRE-EMPH = 0 ---- + cfg.filters.pre_emph = 0; + cfg.plot.custom_colors = DSP(c).color; + cfg.plot.custom_linetypes = {'-'}; + [~, M0] = plot_measurements_gpt(dataTable, cfg); + + % Axis limits + if Mlev == 4 + ylim([1e-5 0.3]); + elseif Mlev == 6 + ylim([6e-4 0.1]); + elseif Mlev == 8 + ylim([9e-4 0.1]); + end + + % ---- FEC lines ---- + yline([2.2e-4 4.85e-3 2e-2], ... + 'LineWidth',1.1, 'Color',[0.2 0.2 0.2], ... + 'LineStyle',':','HandleVisibility','off'); + + beautifyBERplot; + + % ---- Remove redundant labels ---- + if c > 1, ax.YLabel = []; end + if r < rows, ax.XLabel = []; end + + grid(ax,'on'); box(ax,'on'); + end +end + +%% ---- FIXED FIGURE SIZE ---- +pos = 1e3.*[0.1070 0.5497 1.4113 0.6847]; +set(fig, 'Position', pos); + +% %% === EXPORT === +% outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\compare_pre_emphasis.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_EYES.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_EYES.m new file mode 100644 index 0000000..c22e90f --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_EYES.m @@ -0,0 +1,257 @@ +dsp_options.storage_path = 'Z:\2024\sioe_labor\'; +dsp_options.max_occurences = 1; +database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' ); + +rate = [300e9]; +cols = cbrewer2('BuPu',25); +cols = [cols(end-10:2:end,:)]; +cols = cbrewer2('Set1',6); + +fignum = 200; +fig=figure(fignum);clf; + +dbmode = 0; + + +% 1 - PAM 4 with preemphasis +fp = QueryFilter(); +M = 6; +fp.where('Runs', 'pam_level','EQUALS', M); +fp.where('Runs', 'bitrate','EQUALS', rate);%360,390 +fp.where('Runs', 'fiber_length','EQUALS', 2); +fp.where('Runs', 'wavelength','EQUALS', 1310); +fp.where('Runs', 'db_mode','EQUALS', dbmode); +fp.where('Runs', 'rop_attenuation','EQUAL', 0); + +[dataTable,~] = db.queryDB(fp, database.getTableFieldNames('Runs')); + +dataTable = queryRunid(dataTable.run_id, database); +fsym = dataTable.symbolrate; +M = double(dataTable.pam_level); +duob_mode = db_mode(strrep(dataTable.db_mode,'"','')); + +% Load and Sync signal data from DB +[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options); + +% Preprocess signal +Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym); + +Scpe_sig.eye(fsym,M,"fignum",M*10); + +%% === EXPORT TO TIKZ === +% outfile = ['C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\eye_pam_',num2str(M),'.tikz']; +% outfile = ['C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\vnle_optimization.tikz']; +% matlab2tikz(outfile, ... +% 'width','\fwidth', ... +% 'height','\fheight', ... +% 'showInfo',false, ... +% 'extraAxisOptions',{ ... +% 'legend style={font=\footnotesize}', ... +% 'legend columns=1' ... +% } ); + +%% + +if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation + trellexlusion = 1; +else + trellexlusion = 0; +end +mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',3); +len_tr = 4096*2; + +ffe_order = [50, 5, 5]; +dfe_order = [0, 0, 0]; +pf_ncoeffs = 1; +mu_ffe = [0.0001, 0.0008, 0.001]; +mu_dfe = 0.0004; +mu_dc = 0.005; +dc_buffer_len = 1; + +mu_tr = 0; +mu_dd = 0.05; +adaption= 1; +use_dd_mode = 1; +ffe_order = [50, 5, 5]; +eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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); + +dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, ... + 'showAnalysis', 1,... + "postFFE", []); + +%% === FINAL FIGURE SIZE === + +% Existing figure numbers +figEye = 249; +figConst = 341; + +% Find axes in the source figures +srcAxEye = findobj(figEye, 'Type', 'axes'); +srcAxConst = findobj(figConst, 'Type', 'axes'); + +% Create new combined figure +figCombined = figure; +t = tiledlayout(figCombined, 1, 2); +t.TileSpacing = 'compact'; +t.Padding = 'compact'; + +% ------------------------------------------------------------ +% LEFT TILE: EYE DIAGRAM +% ------------------------------------------------------------ +ax1 = nexttile(t, 1); +hold(ax1, 'on') + +% Copy children (images, lines, patches, hist objects, etc.) +copyobj(srcAxEye.Children, ax1); + +% Copy labels and title +ax1.XLabel.String = srcAxEye.XLabel.String; +ax1.YLabel.String = srcAxEye.YLabel.String; +ax1.Title.String = srcAxEye.Title.String; + +% Copy axis limits +ax1.XLim = srcAxEye.XLim; +ax1.YLim = srcAxEye.YLim; +ax1.YDir = srcAxEye.YDir; + +% Copy ticks + labels EXACTLY (including remapped/scaled ones) +ax1.XTick = srcAxEye.XTick; +ax1.XTickLabel = srcAxEye.XTickLabel; +ax1.YTick = srcAxEye.YTick; +ax1.YTickLabel = srcAxEye.YTickLabel; + +% Copy colormap + clim (important for density eye) +colormap(ax1, colormap(srcAxEye.Parent)); +ax1.CLim = srcAxEye.CLim; + +% Copy any style props that matter +ax1.TickDir = srcAxEye.TickDir; +ax1.TickLength = srcAxEye.TickLength; +ax1.FontSize = srcAxEye.FontSize; +ax1.Box = srcAxEye.Box; + +grid(ax1,'on'); + + +% ------------------------------------------------------------ +% RIGHT TILE: CONSTELLATION HISTOGRAM +% ------------------------------------------------------------ +ax2 = nexttile(t, 2); +hold(ax2, 'on') + +copyobj(srcAxConst.Children, ax2); + +% Copy labels and title +ax2.XLabel.String = srcAxConst.XLabel.String; +ax2.YLabel.String = srcAxConst.YLabel.String; +ax2.Title.String = srcAxConst.Title.String; + +% The histogram uses the same y-axis as the eye +% Extract mapping from eye +rawTicks = ax1.YTick; +rawLabelsCell = ax1.YTickLabel; +trueVoltages = str2double(rawLabelsCell); + +% Apply true voltages to the histogram axis +ax2.XTick = flip(trueVoltages); +ax2.XTickLabel = flip(rawLabelsCell); + +% Set histogram y-limits to match the actual voltages +ax2.XLim = [min(trueVoltages) max(trueVoltages)]; + +% Ensure eye diagram prints the same (we *do not* touch ax1.YLim) +ax1.XTickLabel = rawLabelsCell; + + +% Copy colormap (your histogram uses same palette) +colormap(ax2, colormap(srcAxConst.Parent)); + +% Style properties +ax2.TickDir = srcAxConst.TickDir; +ax2.TickLength = srcAxConst.TickLength; +ax2.FontSize = srcAxConst.FontSize; +ax2.Box = srcAxConst.Box; + +grid(ax2,'on'); + +% ============================================================ +% remove right y-axis completely +% ============================================================ +ax2.XAxis.Visible = 'off'; % hides ticks + labels + axis line + +% BUT we still keep the YTick positions internally for alignment: +% ax2.YTick = ; + + +% ============================================================ +% minimize distance between the two plots +% ============================================================ +t.TileSpacing = 'none'; % no space between tiles +t.Padding = 'none'; % no outer padding + +% Also reduce internal padding for each axis +ax1.Position(3) = ax1.Position(3) + 0.02; % widen eye a bit +ax2.Position(1) = ax2.Position(1) - 0.02; % pull histogram closer + + +% Keep left axis grid visible +ax2.YGrid = 'off'; + +% +% ===================================================================== +% FINAL POLISHING: unified visual style +% ======================================================================= + +% --- unified font size --- +FS = 12; +set([ax1 ax2], 'FontSize', FS); + +% --- unified axis line width (outline stroke thickness) --- +LW = 1.0; +set([ax1 ax2], 'LineWidth', LW); + +% --- unified tick length --- +TL = [.015 .015]; +set([ax1 ax2], 'TickLength', TL); + +% --- unified grid style --- +set([ax1 ax2], 'XGrid', 'on', 'YGrid', 'on'); +set([ax1 ax2], 'GridLineStyle', '--'); +set([ax1 ax2], 'GridAlpha', 0.2); + +% --- remove right y-axis ticks and labels --- +ax2.YAxis.Visible = 'off'; + +% --- copy colormap + CLim from the eye to histogram (synchronize look) --- +colormap(ax1, colormap(srcAxEye.Parent)); +colormap(ax2, colormap(srcAxEye.Parent)); +ax2.CLim = ax1.CLim; + +% --- minimal spacing between tiles --- +t.TileSpacing = 'none'; +t.Padding = 'none'; + + +% --- pull the panels together (touching boundary effect) --- +pos1 = ax1.Position; +pos2 = ax2.Position; + +% Shift histogram left until the outlines touch +pos2(1) = pos1(1) + pos1(3) - 0.002; % 0.002 = fine overlap control +ax2.Position = pos2; + +% Expand histogram slightly, remove white band +pos2 = ax2.Position; +pos2(3) = pos2(3) + 0.01; +ax2.Position = pos2; + +% Ensure the left plot stays correct after the move +ax1.Position = pos1; + +% --- enforce same visible outline --- +% For ax2, create a fake left spine (since YAxis is hidden) +ax2.Box = 'on'; % keep outline but no ticks on the right +ax1.Box = 'on'; + +ax2.View = [90 -90]; \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_NGMI.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_NGMI.m new file mode 100644 index 0000000..cf3e317 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_NGMI.m @@ -0,0 +1,221 @@ +%% ============================================================ +% GRID: NGMI, AIR, HD-NetRate, SD-NetRate (1 × 4) +% ============================================================ + +db = DBHandler("dataBase","labor_highspeed","type","mysql"); + +%% --- Base DB Filters (shared across all curves) +fp = QueryFilter(); +fp.where('Runs','fiber_length','EQUALS', 2); +fp.where('Runs','wavelength','EQUALS', 1310); +fp.where('Runs','rop_attenuation','EQUALS', 0); +fp.where('Runs','is_mpi','EQUALS', 0); + +fields = db.getTableFieldNames('dashboard_ungrouped_alltime'); +[dataTable,~] = db.queryDB(fp, fields); + + +%% === Curve Definitions ======================================= +curves = struct; + +% PAM-8 — VNLE PF MLSE — no_emph = 1 — RED +curves(1).pam = 8; +curves(1).eq = equalizer_structure.vnle_pf_mlse; +curves(1).pre = 0; +curves(1).color = clr.Paired.red; +curves(1).mkr = 'o'; + +% PAM-6 — VNLE PF MLSE — no_emph = 1 — BLUE +curves(2).pam = 6; +curves(2).eq = equalizer_structure.vnle_pf_mlse; +curves(2).pre = 1; +curves(2).color = clr.Paired.blue; +curves(2).mkr = 'square'; + +% PAM-4 — VNLE DB MLSE — pre_emph = 0 — GREEN +curves(3).pam = 4; +curves(3).eq = equalizer_structure.vnle_db_mlse; +curves(3).pre = 0; +curves(3).color = clr.Paired.green; +curves(3).mkr = 'diamond'; + +%% === Prepare Analysis Config ================================== +base = struct; +base.group_by = {'equalizer_structure','pre_emph'}; +base.x_axis = 'symbolrate'; +base.outlier = 'none'; +base.show_raw = false; +base.filters = struct; % will be filled per curve + + +%% === Precompute All Curves ==================================== +results = struct; + +for k = 1:numel(curves) + + % --- BER --- + cfg = base; + cfg.y_axis = 'BER'; + cfg.agg = 'min'; + cfg.filters = struct('pam_level', curves(k).pam, ... + 'equalizer_structure', curves(k).eq, ... + 'pre_emph', curves(k).pre); + + A = analyze_measurements_gpt(dataTable, cfg); + cfg.x_axis = 'grossrate'; + B = analyze_measurements_gpt(dataTable, cfg); + + results(k).baudr = A.group{1}.x; + results(k).gross = B.group{1}.x; + + if curves(k).pam == 4 + results(k).ber = A.group{1}.y_precoded; + else + results(k).ber = A.group{1}.y; + end + + % --- NGMI --- + cfg.y_axis = 'NGMI'; + cfg.agg = 'max'; + A = analyze_measurements_gpt(dataTable, cfg); + results(k).ngmi = A.group{1}.y; + + + + % --- AIR --- + cfg.y_axis = 'AIR'; + cfg.agg = 'max'; + A = analyze_measurements_gpt(dataTable, cfg); + results(k).air = A.group{1}.y; + results(k).air = results(k).ngmi .* results(k).gross; + + % --- Net Rates --- + tp = TransmissionPerformance; + results(k).ndr = tp.calculateNetRate(results(k).gross, ... + 'NGMI', results(k).ngmi, ... + 'BER', results(k).ber); +end + + +%% ============================================================ +% FIGURE: 1 × 4 GRID +% ============================================================ +fig = figure(71); clf; +t = tiledlayout(1,4, 'TileSpacing','compact', 'Padding','compact'); + +lw = 1.0; + +% === NGMI vs Grossrate === +ax = nexttile(t,1); +hold on; +for k = 1:3 + plot(results(k).baudr, results(k).ngmi, ... + 'LineWidth', lw, ... + 'Color', curves(k).color, ... + 'MarkerSize', 1, ... + 'MarkerFaceColor', curves(k).color,... + 'Marker',curves(k).mkr); +end +ylabel('NGMI'); +xlabel('Baud rate [GBd]'); +xlim([100 210]); +xticks(100:15:225); +ylim([0.9, 1]); +grid minor; box on; +beautifyBERplot("logscale",0,"setmarkers",0); + + +% === AIR vs Grossrate === +ax = nexttile(t,2); +hold on; +for k = 1:3 + plot(results(k).baudr, results(k).air, ... + '-', 'LineWidth', lw, ... + 'Color', curves(k).color, ... + 'MarkerSize', 2, ... + 'MarkerFaceColor', curves(k).color,'Marker',curves(k).mkr); +end +ylabel('AIR [Gb/s]'); +xlabel('Baud rate [GBd]'); +ylim([280 430]); +yticks(280:30:440) +xlim([100 210]); +xticks(100:15:225); +grid minor; box on; +beautifyBERplot("logscale",0,"setmarkers",0); +yline(400,'LineStyle','--'); + +% === SD-FEC Net Rate === +ax = nexttile(t,3); +hold on; +for k = 1:3 + plot(results(k).baudr, results(k).ndr.SDHD.NetRate, ... + 'LineWidth', lw, ... + 'Color', curves(k).color, ... + 'MarkerSize', 2, ... + 'MarkerFaceColor', curves(k).color,... + 'Marker',curves(k).mkr); +end +ylabel('NDR [Gb/s]'); +xlabel('Baud rate [GBd]'); +ylim([280 430]); +yticks(280:30:440) +xlim([100 210]); +xticks(100:15:225); +grid minor; box on; +beautifyBERplot("logscale",0,"setmarkers",0); +yline(400,'LineStyle','--'); + +% === HD-FEC Net Rate === +ax = nexttile(t,4); +hold on; +for k = 1:3 + % plot(results(k).baudr, results(k).ndr.STAIR.NetRate, ... + % '-', 'LineWidth', lw, ... + % 'Color', curves(k).color, ... + % 'MarkerSize', 4,'Marker','+', ... + % 'MarkerFaceColor', curves(k).color); + + plot(results(k).baudr, results(k).ndr.O_FEC.NetRate, ... + ':', 'LineWidth', lw, ... + 'Color', curves(k).color, ... + 'MarkerSize', 2,... + 'MarkerFaceColor', curves(k).color,... + 'Marker',curves(k).mkr); + + plot(results(k).baudr, results(k).ndr.KP4_hamming.NetRate, ... + '--', 'LineWidth', lw, ... + 'Color', curves(k).color, ... + 'MarkerSize', 2,'Marker','diamond', ... + 'MarkerFaceColor', curves(k).color,... + 'Marker',curves(k).mkr); +end + +yline(400,'LineStyle','--'); +ylabel(''); +xlabel('Baud rate [GBd]'); +ylim([280 430]); +yticks(280:30:440) +xlim([100 210]); +xticks(100:15:225); +grid minor; box on; +beautifyBERplot("logscale",0,"setmarkers",0); + +% === FINAL FIGURE SIZE === +pos = 1e3.*[0.7950 1.1150 1.4113 0.1900]; +set(fig, 'Position', pos); + +% % % %% === EXPORT === +outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\compare_ndr_v3.tikz'; +matlab2tikz(outfile, ... + 'width','\fwidth', ... + 'height','\fheight', ... + 'showInfo',false, ... + 'extraAxisOptions',{ ... + 'legend style={font=\footnotesize}', ... + 'legend columns=1' ... + 'every axis/.append style={font=\scriptsize}',... + 'minor grid style={line width=0.2pt, solid, color=black!10}',... + 'grid style={line width=0.4pt, solid, color=black!20}',... + 'grid style={dashed}',... + }); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_NGMI_v2.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_NGMI_v2.m new file mode 100644 index 0000000..a690207 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_NGMI_v2.m @@ -0,0 +1,180 @@ +%% ============================================================ +% GRID (1 × 4): +% 1) NGMI overview (PAM4+PAM6+PAM8 superimposed) +% 2) PAM-4 tile (AIR + SD-NDR + HD-NDR) +% 3) PAM-6 tile +% 4) PAM-8 tile +% ============================================================ + +db = DBHandler("dataBase","labor_highspeed","type","mysql"); + +%% --- Base DB Filters (shared across all curves) +fp = QueryFilter(); +fp.where('Runs','fiber_length','EQUALS', 2); +fp.where('Runs','wavelength','EQUALS', 1310); +fp.where('Runs','rop_attenuation','EQUALS', 0); +fp.where('Runs','is_mpi','EQUALS', 0); + +fields = db.getTableFieldNames('dashboard_ungrouped_alltime'); +[dataTable,~] = db.queryDB(fp, fields); + +%% === CURVE DEFINITIONS ================================================= +curves = struct; + +curves(1).pam = 8; +curves(1).eq = equalizer_structure.vnle_pf_mlse; +curves(1).pre = 0; +curves(1).color = clr.Paired.red; + +curves(2).pam = 6; +curves(2).eq = equalizer_structure.vnle_pf_mlse; +curves(2).pre = 1; +curves(2).color = clr.Paired.blue; + +curves(3).pam = 4; +curves(3).eq = equalizer_structure.vnle_db_mlse; +curves(3).pre = 0; +curves(3).color = clr.Paired.green; + +% === ANALYSIS ENGINE (extract BER/NGMI/AIR/netrates) =================== +base = struct; +base.group_by = {'equalizer_structure','pre_emph'}; +base.x_axis = 'grossrate'; +base.outlier = 'none'; +base.show_raw = false; + +results = struct; + +for k = 1:numel(curves) + + % ========== BER ========== + cfg = base; + cfg.y_axis = 'BER'; + cfg.agg = 'min'; + + cfg.filters = struct('pam_level', curves(k).pam, ... + 'equalizer_structure', curves(k).eq, ... + 'pre_emph', curves(k).pre); + A = analyze_measurements_gpt(dataTable, cfg); + + results(k).gross = A.group{1}.x; + if curves(k).pam == 4 + results(k).ber = A.group{1}.y_precoded; + else + results(k).ber = A.group{1}.y; + end + + % ========== NGMI ========== + cfg.y_axis = 'NGMI'; cfg.agg = 'max'; + A = analyze_measurements_gpt(dataTable, cfg); + results(k).ngmi = A.group{1}.y; + + % ========== AIR ========== + cfg.y_axis = 'AIR'; cfg.agg = 'max'; + A = analyze_measurements_gpt(dataTable, cfg); + results(k).air = A.group{1}.y; + + % ========== NET RATES ========== + tp = TransmissionPerformance; + results(k).ndr = tp.calculateNetRate(results(k).gross, ... + 'NGMI', results(k).ngmi, ... + 'BER', results(k).ber); +end + + +% ============================================================ +% FIGURE +% ============================================================ +fig = figure(3); +t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact'); + +lw = 1.7; + +% ======================================================================= +% (1) NGMI OVERVIEW TILE (all 3 curves) +% ======================================================================= +ax = nexttile(t,1); hold on; + +for k = 1:3 + plot(results(k).gross, results(k).ngmi, ... + '-o', 'Color', curves(k).color, ... + 'LineWidth',lw,'MarkerSize',5, ... + 'MarkerFaceColor',curves(k).color); +end + +ylabel('NGMI'); +xlabel('Grossrate [Gb/s]'); +ylim([0.9 1]); % your chosen limits +xlim([300 480]); +xticks(300:30:480) +grid minor; box on; +beautifyBERplot; + +% ======================================================================= +% (2–4) PAM-SPECIFIC TILES: AIR, SD-NDR, HD-NDR +% ======================================================================= + +pam_order = [4 6 8]; % left → right + +for ti = 1:3 + pam_target = pam_order(ti); + ax = nexttile(t, 1+ti); hold on; + + % find matching curve + for k = 1:3 + if curves(k).pam ~= pam_target, continue; end + + col = curves(k).color; + + % AIR + plot(results(k).gross, results(k).air, ... + '-','Color',col,'LineWidth',lw,'Marker','*', ... + 'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','AIR'); + + % SD-based net rate + plot(results(k).gross, results(k).ndr.SDHD.NetRate, ... + '--','Color',col,'LineWidth',lw,'Marker','v', ... + 'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','SD+HD'); + + % HD-based net rate + plot(results(k).gross, results(k).ndr.STAIR.NetRate, ... + ':','Color',col,'LineWidth',lw,'Marker','x', ... + 'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','HD-FEC (Staircase)'); + + % HD-based net rate + plot(results(k).gross, results(k).ndr.O_FEC.NetRate, ... + 'LineStyle','-.','Color',col,'LineWidth',lw,'Marker','+', ... + 'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','O-FEC'); + + % HD-based net rate + plot(results(k).gross, results(k).ndr.KP4_hamming.NetRate, ... + 'LineStyle','-','Color',col,'LineWidth',lw,'Marker','x', ... + 'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','KP4+Hamming'); + end + + ylabel('NDR [Gb/s]'); + xlabel('Grossrate [Gb/s]'); + ylim([300 440]); % your chosen limits + yticks(300:20:480) + xlim([300 480]); + xticks(300:30:480) + grid minor; box on; + beautifyBERplot; + yline(400,'HandleVisibility','off'); +end + +% === FIX FIGURE SIZE FOR TIKZ ========================================== +if 0 +pos = 1e3.*[0.3643 0.9943 1.4113 0.2120]; +set(fig,'Position',pos); + +% outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\compare_ndr.tikz'; +% matlab2tikz(outfile, ... +% 'width','\fwidth', ... +% 'height','\fheight', ... +% 'showInfo',false, ... +% 'extraAxisOptions',{ ... +% 'legend style={font=\footnotesize}', ... +% 'legend columns=1' ... +% }); +end \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_ROP.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_ROP.m new file mode 100644 index 0000000..5a3c530 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_ROP.m @@ -0,0 +1,153 @@ +%% ============================================================ +% LOAD DATA (PAM-4, sweep over ROP) +% ============================================================ +database_type = 'mysql'; +db = DBHandler("dataBase", "labor_highspeed", "type", database_type); + +pam_level = 4; +fiberL = 1; % 1 km +wlen = 1310; +baudrate = 360e9; + +fp = QueryFilter(); +fp.where('Runs','pam_level','EQUALS', pam_level); +fp.where('Runs','fiber_length','EQUALS', fiberL); +fp.where('Runs','wavelength','EQUALS', wlen); +fp.where('Runs','bitrate','EQUALS', baudrate); +fp.where('Runs','power_pd_in','LESS_THAN', 7); + +fields = [ + db.getTableFieldNames('power_state_info'); + db.getTableFieldNames('dashboard_ungrouped_alltime') +]; + +[dataTable,~] = db.queryDB(fp, fields); + + +%% ============================================================ +% DSP SCHEMES (Best combinations only) +% ============================================================ +curves = struct; + +curves(1).name = 'VNLE'; +curves(1).eq = equalizer_structure.vnle; +curves(1).color = clr.Paired.red; + +curves(2).name = 'PF + MLSE'; +curves(2).eq = equalizer_structure.vnle_pf_mlse; +curves(2).color = clr.Paired.green; + +curves(3).name = 'DB-target + MLSE'; +curves(3).eq = equalizer_structure.vnle_db_mlse; +curves(3).color = clr.Paired.blue; + +curves(4).name = 'ML-based MLSE'; +curves(4).eq = equalizer_structure.ml_mlse; +curves(4).color = clr.Paired.purple; + + + + +%% ============================================================ +% ANALYSIS ENGINE (No plotting) +% ============================================================ +results = struct; + +for k = 1:numel(curves) + + pre_emph = decide_preemph(pam_level,curves(k).eq); + precoded = decide_precoded(pam_level,curves(k).eq); + + cfg = struct; + cfg.x_axis = 'power_mzm'; % ROP axis + cfg.y_axis = 'BER'; + cfg.agg = 'min'; + cfg.outlier = 'none'; + cfg.show_raw = false; + + cfg.filters = struct( ... + 'pam_level', pam_level, ... + 'fiber_length', fiberL, ... + 'wavelength', wlen, ... + 'bitrate', baudrate, ... + 'is_mpi', 0, ... + 'equalizer_structure', curves(k).eq, ... + 'pre_emph', pre_emph); + + A = analyze_measurements_gpt(dataTable, cfg); + + results(k).x = A.group{1}.x; + if precoded + results(k).ber = A.group{1}.y_precoded; + else + results(k).ber = A.group{1}.y; + end +end + + +%% ============================================================ +% PLOT — BER vs ROP (Single Axis) +% ============================================================ +fig = figure(); clf; hold on; + +lw = 2.0; +ms = 7; + +for k = 1:numel(curves) + plot(results(k).x, results(k).ber, ... + '-o', ... + 'Color', curves(k).color, ... + 'MarkerFaceColor', curves(k).color, ... + 'MarkerSize', ms, ... + 'LineWidth', lw, ... + 'DisplayName', curves(k).name); +end + +set(gca,'YScale','log'); +grid on; + +xlabel('ROP / Power (MZM) [dBm]'); +ylabel('BER'); + +ylim([1e-4 2e-1]); + +title(sprintf('BER vs ROP — PAM-%d, %.0f km, %.0f GBd, %.0f nm', ... + pam_level, fiberL, baudrate*1e-9, wlen)); + +legend('Location','best'); +beautifyBERplot(); + +pos = 1e3.*[0.2 0.6 1.3 0.4]; +set(fig, 'Position', pos); + +%% ============================================================ +% DECISION LOGIC (INLINE FUNCTIONS) +% ============================================================ + +function pe = decide_preemph(M, eq) + % PRE-EMPH RULES: + switch M + case 4 + if eq == equalizer_structure.vnle + pe = 1; % PAM4: VNLE → pre-emph on + else + pe = 0; % PAM4: all others → off + end + case {6,8} + pe = 1; % PAM6/8: all → pre-emph on + otherwise + pe = 0; + end +end + + +function flag = decide_precoded(M, eq) + % PRE-CODE RULES: + if eq == equalizer_structure.vnle_db_mlse + flag = 1; % Always for DB-target + elseif eq == equalizer_structure.ml_mlse && M == 4 + flag = 1; % PAM4: ML-based → precoded + else + flag = 0; + end +end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_Spectra.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_Spectra.m new file mode 100644 index 0000000..97dc7a9 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_Spectra.m @@ -0,0 +1,182 @@ +dsp_options.storage_path = 'Z:\2024\sioe_labor\'; +dsp_options.max_occurences = 1; +database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' ); + +rates = [300e9]; +cols = cbrewer2('BuPu',25); +cols = [cols(end-10:2:end,:)]; +cols = cbrewer2('Set1',6); + +fignum = 200; +fig=figure(fignum);clf; + +for dbmode = 0:1%length(rates) + + + if 0 + rcalpha = 0.05; + fsym = rates/2; + pulsef = 1; + Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); + + Pamsource = PAMsource(... + "fsym",fsym,"M",4,"order",18,"useprbs",0,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.2,... + "applypulseform",pulsef,"pulseformer",Pform,... + "randkey",20,... + "db_precode",dbmode,"db_encode",0,... + "mrds_code",0,"mrds_blocklength",512); + + [Digi_sig,Symbols,Bits] = Pamsource.process(); + + Digi_sig = Digi_sig.normalize("mode","rms"); + + %%% 1) PLOT FULL RESPONSE SIGNAL + Digi_sig.spectrum("displayname","Full Response","fignum",fignum+dbmode,"normalizeToNyquist",0,"normalizeTo0dB",0,"color",[0.2,0.2,0.2],"linestyle",'-','addDCoffset',0,'normalizeToDC',1); + + + %%% 2) PLOT PREEMPH. TX SIGNAL + if dbmode == 0 + maxamp = -37; + precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs); + + precomp_path = "C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\precomp"; + precomp_fn = "lab_high_speed"; + Digi_sig_pre = precomp_est.precomp(Digi_sig,'maxampdb',maxamp,'loadPath',precomp_path,'fileName',precomp_fn); + + Digi_sig_pre = Digi_sig_pre.resample("fs_out",fdac); + + Digi_sig_pre= Digi_sig_pre.normalize("mode","rms"); + + Digi_sig_pre.spectrum("displayname","Strong Precomp","fignum",fignum+dbmode,"normalizeToNyquist",0,"normalizeTo0dB",0,"color",[0,0,0],"linestyle",'-.','addDCoffset',0,'normalizeToDC',1); + end + + end + + % 1 - PAM 4 with preemphasis + fp = QueryFilter(); + M = 4; + fp.where('Runs', 'pam_level','EQUALS', M); + fp.where('Runs', 'bitrate','EQUALS', rates);%360,390 + fp.where('Runs', 'fiber_length','EQUALS', 10); + fp.where('Runs', 'wavelength','EQUALS', 1322.7); %1327.4 + fp.where('Runs', 'db_mode','EQUALS', dbmode); + fp.where('Runs', 'rop_attenuation','EQUAL', 0); + + [dataTable,~] = database.queryDB(fp, database.getTableFieldNames('Runs')); + + dataTable = queryRunid(dataTable.run_id, database); + fsym = dataTable.symbolrate; + M = double(dataTable.pam_level); + + % Load and Sync signal data from DB + [Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options); + + % Preprocess signal + Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym); + Scpe_sig = Scpe_cell{1}; + + %%% 3) PLOT DB Tgt. SIGNAL + if 1 + DB_Symbols = Duobinary().encode(Symbols); + DB_Symbols.spectrum("fignum",fignum+dbmode,"normalizeTo0dB",1,"displayname",'DB-Response','addDCoffset',0,'color',clr.Set1.blue,'normalizeToNyquist',0,'linestyle','--'); + end + + %%% 4) Plot RX Signal + Scpe_sig.spectrum("fignum",fignum+dbmode,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',1,'color',[0,0,0],'normalizeToNyquist',0,'linestyle',':'); +Scpe_sig.eye(fsym,M,"fignum",47,"displayname",' Eye of AVG Signal'); + % xline(Symbols.fs/2.*1e-9,'Color',cols(r,:),'HandleVisibility','off'); + + average_signals = 1; + if average_signals + Scpe_sig_avg = Scpe_sig; + scope_mean = zeros(size(Scpe_cell{1}.signal)); + for n=1:numel(Scpe_cell) + scope_mean = scope_mean + Scpe_cell{n}.signal; + end + scope_mean = scope_mean ./ n; + Scpe_sig_avg.signal = scope_mean; + + Scpe_sig_avg.spectrum("displayname","Scope PSD","fignum",20,"normalizeTo0dB",1); + Scpe_sig_avg.plot("displayname","Scope raw signal","fignum",27,"clear",1); + Scpe_sig_avg.eye(fsym,M,"fignum",48,"displayname",' Eye of AVG Signal'); + end + + + fig = figure(fignum+dbmode); + if dbmode == 0 + ylim([-22,12]); + else + ylim([-22,2]); + end + xlim([0,105]); + xticks(-100:20:100); + yticks(-20:10:10); + + beautifyBERplot("logscale",0,"setmarkers",0) + pos = [100.3333 991.6667 358.0000 192.6667]; + set(fig, 'Position', pos); + + %%%%%%%%%%%% + drawnow; + + % Do EQ and find alpha's + len_tr = 4096*2; + + ffe_order = [50, 5, 5]; + dfe_order = [0, 0, 0]; + pf_ncoeffs = 1; + mu_ffe = [0.0001, 0.0008, 0.001]; + mu_dfe = 0.0004; + mu_dc = 0.005; + + %%% FULL RESP TARGET + eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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); + pf_1 = Postfilter("ncoeff",1,"useBurg",1); + + [eq_signal_sd, eq_noise] = eq_.process(Scpe_sig, Symbols); + + % eq_noise.signal = eq_noise.signal - mean(eq_noise.signal); + % eq_noise = eq_noise.normalize("mode","rms"); + + [mlse_sig_sd,whitened_noise] = pf_1.process(eq_signal_sd, eq_noise); + + fig = figure(fignum+dbmode+10); hold on + + [h, w] = freqz(1, pf_1.coefficients, length(eq_noise), "whole", eq_noise.fs); + h = h / max(abs(h)); % Normalize the filter response + w_ = (w - eq_noise.fs / 2); + + %%% DB TARGET + db_ref_sequence = Duobinary().encode(Symbols); + eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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_signal, db_noise] = eq_.process(Scpe_sig,db_ref_sequence); + + % db_noise.signal = db_noise.signal - mean(db_noise.signal); + % db_noise = db_noise.normalize("mode","rms"); + + %%% 1-3) Plot EQ Noise EEN + figure(fignum+dbmode+10) + eq_noise.spectrum("displayname", 'Noise', "fignum", fignum+dbmode+10, "normalizeTo0dB", 0,"color",clr.Set1.green,"normalizeToDC",0,"addDCoffset",0); + if dbmode == 1 + offset = 27.7; + else + offset = 29.8; + end + plot(w_ * 1e-9, 20 * log10(fftshift(abs(h)))-offset, 'DisplayName', ['Burg Coeffs: ', num2str(round(pf_1.coefficients, 2)), ' '], 'LineWidth', 1,'Color',clr.Set1.green,'LineStyle','--'); + db_noise.spectrum("displayname", 'DBt. Noise', "fignum", fignum+dbmode+10, "normalizeTo0dB", 0,"color",clr.Set1.blue,"normalizeToDC",0,"addDCoffset",0); + + ylim([-54,-25]); + xlim([0,105]); + xticks(0:20:110); + yticks(-50:10:10); + + beautifyBERplot("logscale",0,"setmarkers",0) + pos = [100.3333 991.6667 358.0000 192.6667]; + set(fig, 'Position', pos); + +end + + +% === FINAL FIGURE SIZE === diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_WAVELENGTH.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_WAVELENGTH.m new file mode 100644 index 0000000..ffac5d2 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_WAVELENGTH.m @@ -0,0 +1,124 @@ +%% ============================================================ +% LOAD DATA +% ============================================================ +database_type = 'mysql'; +db = DBHandler("dataBase", "labor_highspeed", "type", database_type); + +M = 4; % PAM level for this analysis + +fp = QueryFilter(); +fp.where('Runs', 'pam_level', 'EQUALS', M); +fp.where('Runs', 'fiber_length', 'EQUALS', 10); +fp.where('Runs', 'bitrate', 'EQUALS', 360e9); +fp.where('Runs', 'is_mpi', 'EQUALS', 0); + +fields = [ + db.getTableFieldNames('power_state_info'); + db.getTableFieldNames('dashboard_ungrouped_alltime') +]; + +[dataTable, ~] = db.queryDB(fp, fields); + + +%% ============================================================ +% COMMON CONFIGURATION FOR ALL SUBPLOTS +% ============================================================ +%% ============================================================ +% DEFINE DSP ALGORITHMS FOR THE 4 SUBPLOTS +% ============================================================ +curves = struct; + +curves(1).name = 'VNLE'; +curves(1).eq = equalizer_structure.vnle; +curves(1).pre = 0; +curves(1).color = clr.Paired.red; + +curves(2).name = 'PF + MLSE'; +curves(2).eq = equalizer_structure.vnle_pf_mlse; +curves(2).pre = 0; +curves(2).color = clr.Paired.green; + +curves(3).name = 'DB-target + MLSE'; +curves(3).eq = equalizer_structure.vnle_db_mlse; +curves(3).pre = 0; +curves(3).color = clr.Paired.blue; + +curves(4).name = 'ML-based MLSE'; +curves(4).eq = equalizer_structure.ml_mlse; +if M == 4 +curves(4).pre = 0; +else +curves(4).pre = 1; +end +curves(4).color = clr.Paired.purple; + + +%% ============================================================ +% ANALYSIS ENGINE — NO PLOTTING +% ============================================================ +results = struct; + +for k = 1:numel(curves) + + %% ---- BASE CONFIG ---- + cfg = struct; + cfg.x_axis = 'wavelength'; + cfg.y_axis = 'BER'; + cfg.agg = 'min'; + cfg.outlier = 'none'; + % cfg.group_by = {'wavelength'}; + cfg.show_raw = false; + + cfg.filters = struct( ... + 'pam_level', M, ... + 'is_mpi', 0, ... + 'bitrate', 360e9, ... + 'fiber_length', 10, ... + 'equalizer_structure', curves(k).eq, ... + 'pre_emph', curves(k).pre); + + %% ---- GET BER ---- + cfg.y_axis = 'BER'; + A = analyze_measurements_gpt(dataTable, cfg); + + results(k).wavelength = A.group{1}.x; + + if curves(k).eq == equalizer_structure.vnle_db_mlse || ... + curves(k).eq == equalizer_structure.ml_mlse + % DB and ML-based need precoded BER + results(k).ber = A.group{1}.y_precoded; + else + results(k).ber = A.group{1}.y; + end + +end + +%% ============================================================ +% 1×4 TILED BER-vs-WAVELENGTH FIGURE +% ============================================================ +fig=figure(901); +tiledlayout(1,4,'TileSpacing','compact','Padding','compact'); + +lw = 1.8; % line width +ms = 6; % marker size + +for k = 1:numel(curves) + nexttile; hold on; + + plot(results(k).wavelength, results(k).ber, ... + '-o', ... + 'Color', curves(k).color, ... + 'MarkerFaceColor', curves(k).color, ... + 'MarkerSize', ms, ... + 'LineWidth', lw); + + set(gca,'YScale','log'); + grid on; + xlabel('wavelength'); + ylabel('BER'); + title(curves(k).name); + ylim([1e-4, 0.1]) + beautifyBERplot(); +end +pos = 1e3.*[0.1070 0.5497 1.4113 0.3253]; +set(fig, 'Position', pos); \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_WAVELENGTH_VS_BAUDRATE.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_WAVELENGTH_VS_BAUDRATE.m new file mode 100644 index 0000000..d987359 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_WAVELENGTH_VS_BAUDRATE.m @@ -0,0 +1,160 @@ +%% ============================================================ +% PARAMETERS +% ============================================================ +database_type = 'mysql'; +db = DBHandler("dataBase", "labor_highspeed", "type", database_type); + +pam_level = 4; % FIXED for this figure +baudrates = [300e9 330e9 360e9 390e9]; +fiberL = 10; + +fields = [ + db.getTableFieldNames('power_state_info'); + db.getTableFieldNames('dashboard_ungrouped_alltime') +]; + +%% ============================================================ +% DEFINE DSP SCHEMES +% ============================================================ +curves = struct; + +curves(1).name = 'VNLE'; +curves(1).eq = equalizer_structure.vnle; +curves(1).color = clr.Paired.red; + +curves(2).name = 'PF + MLSE'; +curves(2).eq = equalizer_structure.vnle_pf_mlse; +curves(2).color = clr.Paired.green; + +curves(3).name = 'DB-target + MLSE'; +curves(3).eq = equalizer_structure.vnle_db_mlse; +curves(3).color = clr.Paired.blue; + +curves(4).name = 'ML-based MLSE'; +curves(4).eq = equalizer_structure.ml_mlse; +curves(4).color = clr.Paired.purple; + + +%% ============================================================ +% ANALYSIS — results(b, k): b = baudrate index, k = DSP scheme index +% ============================================================ +results = struct; + +for b = 1:length(baudrates) + + Rb = baudrates(b); + + % --- query matching runs --- + fp = QueryFilter(); + fp.where('Runs','pam_level','EQUALS', pam_level); + fp.where('Runs','fiber_length','EQUALS', fiberL); + fp.where('Runs','bitrate','EQUALS', Rb); + fp.where('Runs','is_mpi','EQUALS', 0); + + [dataTable, ~] = db.queryDB(fp, fields); + + for k = 1:numel(curves) + + %% ---- DECIDE PRE-EMPH & PRECoded RULES for PAM-4 ---- + pre_emph = decide_preemph(pam_level, curves(k).eq); + use_precoded = decide_precoded(pam_level, curves(k).eq); + + %% ---- SETUP ANALYSIS CONFIG ---- + cfg = struct; + cfg.x_axis = 'wavelength'; + cfg.y_axis = 'BER'; + cfg.agg = 'min'; + cfg.outlier = 'none'; + % cfg.group_by = {'wavelength'}; + cfg.show_raw = false; + + cfg.filters = struct( ... + 'pam_level', pam_level, ... + 'is_mpi', 0, ... + 'bitrate', Rb, ... + 'fiber_length', fiberL, ... + 'equalizer_structure', curves(k).eq, ... + 'pre_emph', pre_emph); + + %% ---- RUN ANALYSIS ---- + A = analyze_measurements_gpt(dataTable, cfg); + + results(b,k).wavelength = A.group{1}.x; + + if use_precoded + results(b,k).ber = A.group{1}.y_precoded; + else + results(b,k).ber = A.group{1}.y; + end + end +end + + +%% ============================================================ +% PLOT — 1×4 (one tile per baudrate) +% ============================================================ +fig = figure(); clf; +tiledlayout(1,4,'TileSpacing','compact','Padding','compact'); + +lw = 1.8; +ms = 6; + +for b = 1:length(baudrates) + nexttile; hold on; + + for k = 1:numel(curves) + plot(results(b,k).wavelength, results(b,k).ber, ... + '-o', ... + 'Color', curves(k).color, ... + 'MarkerFaceColor', curves(k).color, ... + 'MarkerSize', ms, ... + 'LineWidth', lw, ... + 'DisplayName', curves(k).name); + end + + set(gca,'YScale','log'); + grid on; + xlabel('Wavelength [nm]'); + ylabel('BER'); + ylim([1e-4 0.1]); + title(sprintf('PAM-%d @ %.0f GBd',pam_level, baudrates(b)/1e9)); + legend('Location','best'); + beautifyBERplot(); +end + +% Optional figure size +pos = 1e3.*[0.1 0.55 1.4 0.32]; +set(fig, 'Position', pos); + + +%% ============================================================ +% DECISION LOGIC (INLINE FUNCTIONS) +% ============================================================ + +function pe = decide_preemph(M, eq) + % PRE-EMPH RULES: + switch M + case 4 + if eq == equalizer_structure.vnle + pe = 1; % PAM4: VNLE → pre-emph on + else + pe = 0; % PAM4: all others → off + end + case {6,8} + pe = 1; % PAM6/8: all → pre-emph on + otherwise + pe = 0; + end +end + + +function flag = decide_precoded(M, eq) + % PRE-CODE RULES: + if eq == equalizer_structure.vnle_db_mlse + flag = 1; % Always for DB-target + elseif eq == equalizer_structure.ml_mlse && M == 4 + flag = 1; % PAM4: ML-based → precoded + else + flag = 0; + end +end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m new file mode 100644 index 0000000..2213c8a --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m @@ -0,0 +1,88 @@ +%% ============================================================ +% PLOT +% ============================================================ +figure; hold on; +ms = 32; % scatter size +lw = 0.8; % line width + +for k = 1:4 % PAM-2/4/6/8 + + M = pam_list(k); + idxPam = (Mvals == M); + + % Extract for this PAM + x = baud(idxPam); + y = netrate(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}; + + if firstLegend + h = 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, ... + '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 + end +end + +grid on; +xlabel('Baud rate [GBd]'); +ylabel('Net rate [Gb/s]'); + +legend('Location','northwest'); +set(gca,'FontSize',11); + + +%% === EXPORT === +outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\highspeedresults.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/HighSpeedExperiments.xlsx b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/HighSpeedExperiments.xlsx new file mode 100644 index 0000000..59d1ab5 Binary files /dev/null and b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/HighSpeedExperiments.xlsx differ diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/analyze_measurements_gpt.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/analyze_measurements_gpt.m new file mode 100644 index 0000000..9ab6e85 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/analyze_measurements_gpt.m @@ -0,0 +1,334 @@ +function [M, cfg] = analyze_measurements_gpt(T, cfg) +% ANALYZE_MEASUREMENTS_GPT +% Filter, compute X/Y, group and aggregate measurements from table T. +% No plotting here. +% +% Usage: +% [M, cfg] = analyze_measurements_gpt(dataTable, cfg); +% +% Typical result (single group): +% M.x -> aggregated x-values (e.g., grossrate) +% M.y -> aggregated y-values (e.g., BER or NGMI) +% M.y_precoded -> aggregated precoded BER (if available) +% +% For multiple groups: +% M.group(g).x, M.group(g).y, M.group(g).label, ... + +%% ---- Defaults (non-plot) ---- +if nargin < 2, cfg = struct; end +defaults = struct( ... + 'x_axis' , 'symbolrate', ... + 'y_axis' , 'BER', ... + 'y_scale' , 'auto', ... + 'group_by' , {{'equalizer_structure','pre_emph'}}, ... + 'filters' , struct, ... + 'agg' , 'mean', ... + 'outlier' , 'auto', ... + 'mad_z' , 4, ... + 'pct_limits' , [2.5 97.5], ... + 'min_pts_x' , 3, ... + 'show_raw' , true, ... + 'show_precoded', [], ... + 'show_spread' , 'none', ... + 'fec_lines' , [], ... + 'plot' , struct() ... % plot settings handled in plot function +); +cfg = filldefaults(cfg, defaults); + +%% ---- Derived/prep columns ---- +if ~ismember('pre_emph', T.Properties.VariableNames) + if ~ismember('db_mode', T.Properties.VariableNames) + error('Missing column "db_mode" for pre_emph derivation.'); + end + T.pre_emph = T.db_mode == 0; +end + +if ~ismember(cfg.y_axis, T.Properties.VariableNames) + error('y_axis "%s" not found in table.', cfg.y_axis); +end + +isBER = startsWith(cfg.y_axis, "BER", 'IgnoreCase', true); +M.isBER = isBER; + +if strcmpi(cfg.y_scale,'auto') + cfg.y_scale = tern(isBER, 'log', 'linear'); +end +if strcmpi(cfg.outlier,'auto') + cfg.outlier = tern(isBER, 'mad', 'none'); +end +if isempty(cfg.show_precoded) + cfg.show_precoded = isBER && ismember('BER_precoded', T.Properties.VariableNames); +end + +%% ---- Filters & core X/Y extraction ---- +T = applyFilters(T, cfg.filters); + +[x_raw, x_label] = computeX(T, cfg.x_axis); +y_raw = T.(cfg.y_axis); + +validXY = isfinite(x_raw) & isfinite(y_raw); +T = T(validXY, :); +x_raw = x_raw(validXY); +y_raw = y_raw(validXY); + +% Degiga if needed +if mean(abs(y_raw)) > 1e8 + y_raw = y_raw .* 1e-9; +end + +if cfg.show_precoded && ismember('BER_precoded', T.Properties.VariableNames) + y_raw_p = T.BER_precoded(validXY); +else + y_raw_p = []; +end + +%% ---- Grouping ---- +group_by = cfg.group_by; +if ~all(ismember(group_by, T.Properties.VariableNames)) + error('Some group_by columns are missing in table.'); +end +[G, grpTbl] = findgroups(T(:, group_by)); +nG = max(G); + +%% ---- Aggregation per group ---- +M = struct; +M.cfg = cfg; +M.x_label = x_label; +M.y_axis = cfg.y_axis; +M.x_axis = cfg.x_axis; +M.nGroups = nG; + +% raw (filtered) data +M.raw = struct; +M.raw.x = x_raw; +M.raw.y = y_raw; +M.raw.y_precoded = y_raw_p; +M.raw.T = T; + +M.group = cell(nG,1); + +useLog = strcmpi(cfg.y_scale,'log'); +for gi = 1:nG + idx = (G == gi); + Ti = T(idx,:); + xi = x_raw(idx); + yi = y_raw(idx); + + [xu, ~, iu] = unique(xi); + yu = nan(size(xu)); + ylo = nan(size(xu)); + yhi = nan(size(xu)); + + for k = 1:numel(xu) + bin = (iu==k); + yy = yi(bin); + yy = yy(isfinite(yy)); + if isempty(yy), continue; end + + km = outlierMask(yy, cfg, useLog); + if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end + yy = yy(km); + + switch lower(cfg.agg) + case 'median' + yu(k) = median(yy,'omitnan'); + case 'mean' + yu(k) = mean(yy,'omitnan'); + case 'min' + yu(k) = min(yy); + case 'max' + yu(k) = max(yy); + otherwise + error('Unknown agg mode "%s".', cfg.agg); + end + + if strcmpi(cfg.show_spread,'iqr') + q = prctile(yy,[25 75]); + ylo(k) = max(yu(k)-q(1), eps); + yhi(k) = max(q(2)-yu(k), eps); + elseif strcmpi(cfg.show_spread,'minmax') + ylo(k) = min(yy); + yhi(k) = max(yy); + end + end + + % sort by x + [xu, ord] = sort(xu); + yu = yu(ord); + ylo = ylo(ord); + yhi = yhi(ord); + + g = struct; + g.label = buildLabel(grpTbl(gi,:), group_by); + g.idx = find(G==gi); + g.T = Ti; + g.x_raw = xi; + g.y_raw = yi; + g.x = xu; + g.y = yu; + g.y_lo = ylo; + g.y_hi = yhi; + g.y_precoded = []; + g.y_precoded_lo = []; + g.y_precoded_hi = []; + + % Precoded aggregation (if requested & available) + if cfg.show_precoded && ~isempty(y_raw_p) && strcmpi(cfg.y_axis,'BER') + ypi = y_raw_p(idx); + ypu = nan(size(xu)); + + for k = 1:numel(xu) + bin = (iu==k); + yy = ypi(bin); + yy = yy(isfinite(yy)); + if isempty(yy), continue; end + + km = outlierMask(yy, cfg, true); + if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end + yy = yy(km); + + switch lower(cfg.agg) + case 'median' + ypu(k) = median(yy,'omitnan'); + case 'mean' + ypu(k) = mean(yy,'omitnan'); + case 'min' + ypu(k) = min(yy); + case 'max' + ypu(k) = max(yy); + end + end + + g.y_precoded = ypu(ord); + end + + M.group{gi} = g; +end + +% Convenience flatten for single-group case +if nG == 1 + g = M.group{1}; + M.x = g.x; + M.y = g.y; + M.y_precoded = g.y_precoded; +end + +end % ===== main ===== + + +%% ===================== Helpers ===================== + +function cfg = filldefaults(cfg, defs) +fn = fieldnames(defs); +for i = 1:numel(fn) + f = fn{i}; + if ~isfield(cfg, f) || isempty(cfg.(f)) + cfg.(f) = defs.(f); + elseif isstruct(defs.(f)) && isstruct(cfg.(f)) + cfg.(f) = filldefaults(cfg.(f), defs.(f)); % recursive + end +end +end + +function out = tern(cond, a, b) +if cond + out = a; +else + out = b; +end +end + +function T2 = applyFilters(T, filters) +if isempty(filters), T2 = T; return; end +keep = true(height(T),1); +fns = fieldnames(filters); +for i = 1:numel(fns) + name = fns{i}; + if ~ismember(name, T.Properties.VariableNames) + warning('Filter column "%s" not found. Ignored.', name); + continue + end + val = filters.(name); + col = T.(name); + if isa(val,'function_handle') + m = val(col); + if ~islogical(m) || ~isequal(size(m), size(col)) + error('Filter for %s must return logical mask of same size.', name); + end + keep = keep & m; + else + keep = keep & ismember(col, val); + end +end +T2 = T(keep,:); +end + +function [x, label] = computeX(T, whichX) +switch lower(whichX) + case {'symbolrate','baudrate'} + x = T.symbolrate * 1e-9; + label = 'Symbol rate [GBd]'; + case 'bitrate' + if ~ismember('pam_level', T.Properties.VariableNames) + error('bitrate requires "pam_level" column.'); + end + bits = floor(log2(double(T.pam_level))*10)/10; + x = (T.symbolrate .* bits) * 1e-9; + label = 'Grossrate [Gb/s]'; + case 'grossrate' + x = T.grossrate * 1e-9; + label = 'Grossrate [Gb/s]'; + otherwise + if ~ismember(whichX, T.Properties.VariableNames) + error('x_axis "%s" not found in table.', whichX); + end + x = T.(whichX); + label = whichX; +end +x = double(x(:)); +end + +function keep = outlierMask(y, cfg, useLog) +if isempty(y), keep = false(size(y)); return; end +y = y(:); +switch lower(cfg.outlier) + case 'none' + keep = true(size(y)); return + case 'mad' + z = tern(useLog, log10(y), y); + med = median(z,'omitnan'); + madv = median(abs(z-med),'omitnan'); + if ~(isfinite(madv) && madv>0) + keep = true(size(y)); return + end + sigma = 1.4826*madv; + zz = tern(useLog, log10(y), y); + keep = abs(zz - med) <= cfg.mad_z*sigma; + case 'pctl' + pr = prctile(y, cfg.pct_limits); + keep = (y >= pr(1)) & (y <= pr(2)); + otherwise + error('Unknown outlier mode "%s".', cfg.outlier); +end +end + +function s = buildLabel(grpRow, group_by) +parts = strings(1, numel(group_by)); +for i = 1:numel(group_by) + key = group_by{i}; + val = grpRow.(key); + if iscell(val), val = val{1}; end + if islogical(val), val = tern(val,'w/','w/o'); end + if key == "equalizer_structure" + key = ''; + val = upper(val); + val = strrep(val,'_',' '); + end + if key == "pre_emph" + val = [val, ' pre-emph.']; + key = ''; + end + parts(i) = sprintf('%s %s', key, string(val)); +end +s = strjoin(parts, ', '); +end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_best_results.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_best_results.m new file mode 100644 index 0000000..5b2e245 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_best_results.m @@ -0,0 +1,240 @@ + + +database_type = 'mysql'; +dataBase = 'labor_highspeed'; +db = DBHandler("dataBase", [dataBase], "type", database_type); + + +% M = 4; +fp = QueryFilter(); +% fp.where('Runs', 'pam_level','EQUALS', M); +fp.where('Runs', 'fiber_length','EQUALS', 2); +fp.where('Runs', 'wavelength','LESS_THAN', 1312); +% fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis +fp.where('Runs', 'rop_attenuation','EQUALS', 0); + +fields = db.getTableFieldNames('power_state_info'); +fields = [fields; db.getTableFieldNames('dashboard_ungrouped_alltime')]; %dashboard_ungrouped_after_nov_2025 dashboard_ungrouped_aug_nov_2025 +[dataTable,~] = db.queryDB(fp, fields); + +%% +cfg = struct; +cfg.x_axis = 'grossrate'; % 'symbol rate' | 'bitrate' | 'wavelength' grossrate +cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ... + +cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise +cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available +cfg.show_raw = false; +cfg.show_spread = 'none'; % 'none' or 'iqr' or minmax +cfg.agg = 'min'; % or 'median' +cfg.show_precoded = 0; + +% cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional +cfg.fec_lines = []; +cfg.plot.custom_colors = [ + clr.Paired.red; + clr.Paired.blue; + clr.Paired.green; + clr.Paired.orange; + clr.Paired.purple + ]; + +cfg.plot.custom_colors_scatter = [ + clr.Paired.lightred; + clr.Paired.lightblue; + clr.Paired.lightgreen; + clr.Paired.lightorange; + clr.Paired.lightpurple + ]; + + +% New styling knobs +cfg.plot.use_cbrewer2 = true; +cfg.plot.colormap = 'Paired'; +cfg.plot.paired_dark_first = false; % dark for lines, light for scatter +cfg.plot.lineWidth = 2.0; +cfg.plot.errWidth = 1.2; +cfg.plot.scatterAlpha = 0.35; +cfg.plot.legendLocation = 'best'; +cfg.plot.fecLineWidth = 2.4; % thicker FEC limits +cfg.plot.lineStyle_pre_emph_on = '-'; +cfg.plot.lineStyle_pre_emph_off = '-'; + +%% PLOT NGMI + +% Common cfg +cfg.show_precoded = 1; +cfg.group_by = {'equalizer_structure','pre_emph'}; +cfg.x_axis = 'grossrate'; +cfg.y_axis = 'NGMI'; % 'BER' | 'GMI' | 'AIR' | ... +cfg.y_scale = 'lin'; +cfg.plot.custom_colors_scatter = []; +cfg.plot.use_cbrewer2 = false; +cfg.fec_lines = []; +cfg.agg = 'max'; + +cfg.figure_number = 45; +% fig = figure(cfg.figure_number); + +lambda = 1310; +% PAM 4 +cfg.plot.custom_colors = clr.Paired.red; +cfg.plot.custom_linetypes = {'-'}; +cfg.filters = struct('is_mpi',0,'pam_level',4, ... + 'equalizer_structure',equalizer_structure.vnle_db_mlse, ... + 'pre_emph',0,'wavelength',lambda); +cfg.show_precoded = 1; +a = plot_measurements_gpt(dataTable, cfg); +ngmi_pam4 = a.lines(1).YData; +grossrates = a.lines(1).XData; +tp = TransmissionPerformance; +netrates_vnle = tp.calculateNetRate(grossrates, ... + 'NGMI', ngmi_pam4, ... + 'BER', BER_VNLE); + +cfg.plot.custom_colors = clr.Paired.red; +cfg.plot.custom_linetypes = {'--'}; +cfg.filters = struct('is_mpi',0,'pam_level',4, ... + 'equalizer_structure',equalizer_structure.vnle_db_mlse, ... + 'pre_emph',0,'wavelength',1293); +cfg.show_precoded = 1; +plot_measurements_gpt(dataTable, cfg); + +% PAM 6 +cfg.plot.custom_colors = clr.Paired.blue; +cfg.plot.custom_linetypes = {'-'}; +cfg.filters = struct('is_mpi',0,'pam_level',6, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',1,'wavelength',lambda); +cfg.show_precoded = 0; +plot_measurements_gpt(dataTable, cfg); + +cfg.plot.custom_colors = clr.Paired.blue; +cfg.plot.custom_linetypes = {'--'}; +cfg.filters = struct('is_mpi',0,'pam_level',6, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',1,'wavelength',1293); +cfg.show_precoded = 0; +plot_measurements_gpt(dataTable, cfg); + +% PAM 8 +cfg.plot.custom_colors = clr.Paired.green; +cfg.plot.custom_linetypes = {'-'}; +cfg.filters = struct('is_mpi',0,'pam_level',8, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',1,'wavelength',lambda); +cfg.show_precoded = 0; +plot_measurements_gpt(dataTable, cfg); + +cfg.plot.custom_colors = clr.Paired.green; +cfg.plot.custom_linetypes = {'--'}; +cfg.filters = struct('is_mpi',0,'pam_level',8, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',1,'wavelength',1293); +cfg.show_precoded = 0; +plot_measurements_gpt(dataTable, cfg); + +beautifyBERplot +ylim([0.87,1.01]); +% xlim([290,480]); + +%% PLOT AIR + +% Common cfg +cfg.show_precoded = 1; +cfg.group_by = {'equalizer_structure','pre_emph'}; +cfg.x_axis = 'grossrate'; +cfg.y_axis = 'AIR'; % 'BER' | 'GMI' | 'AIR' | ... +cfg.y_scale = 'lin'; +cfg.plot.custom_colors_scatter = []; +cfg.plot.use_cbrewer2 = false; +cfg.fec_lines = []; +cfg.agg = 'max'; + +cfg.figure_number = 47; + +lambda = 1310; + +% cfg = struct; +cfg.filters = struct('is_mpi',0,'pam_level',4, ... + 'equalizer_structure',equalizer_structure.vnle_db_mlse, ... + 'pre_emph',0,'wavelength',lambda); +cfg.x_axis = 'grossrate'; +cfg.y_axis = 'NGMI'; % or 'NGMI', etc. +cfg.show_precoded = 1; + +[M, cfg] = analyze_measurements_gpt(dataTable, cfg); + +grossrates = M.x; % aggregated X +ber = M.y; % aggregated Y (BER or NGMI) +ber_prec = M.y_precoded; % precoded BER (if available) + +[h, M] = plot_measurements_gpt(dataTable, cfg); + + +% PAM 4 +cfg.plot.custom_colors = clr.Paired.red; +cfg.plot.custom_linetypes = {'-'}; +cfg.filters = struct('is_mpi',0,'pam_level',4, ... + 'equalizer_structure',equalizer_structure.vnle_db_mlse, ... + 'pre_emph',0,'wavelength',lambda); +cfg.show_precoded = 1; +plot_measurements_gpt(dataTable, cfg); + + + +cfg.plot.custom_colors = clr.Paired.red; +cfg.plot.custom_linetypes = {'--'}; +cfg.filters = struct('is_mpi',0,'pam_level',4, ... + 'equalizer_structure',equalizer_structure.vnle_db_mlse, ... + 'pre_emph',0,'wavelength',1293); +cfg.show_precoded = 1; +plot_measurements_gpt(dataTable, cfg); + +% PAM 6 +cfg.plot.custom_colors = clr.Paired.blue; +cfg.plot.custom_linetypes = {'-'}; +cfg.filters = struct('is_mpi',0,'pam_level',6, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',1,'wavelength',lambda); +cfg.show_precoded = 0; +plot_measurements_gpt(dataTable, cfg); + +cfg.plot.custom_colors = clr.Paired.blue; +cfg.plot.custom_linetypes = {'--'}; +cfg.filters = struct('is_mpi',0,'pam_level',6, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',1,'wavelength',1293); +cfg.show_precoded = 0; +plot_measurements_gpt(dataTable, cfg); + +% PAM 8 +cfg.plot.custom_colors = clr.Paired.green; +cfg.plot.custom_linetypes = {'-'}; +cfg.filters = struct('is_mpi',0,'pam_level',8, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',1,'wavelength',lambda); +cfg.show_precoded = 0; +plot_measurements_gpt(dataTable, cfg); + +cfg.plot.custom_colors = clr.Paired.green; +cfg.plot.custom_linetypes = {'--'}; +cfg.filters = struct('is_mpi',0,'pam_level',8, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',1,'wavelength',1293); +cfg.show_precoded = 0; +plot_measurements_gpt(dataTable, cfg); + +ax = gca; + +beautifyBERplot + +ylim([275,435]); +xlim([290,480]); + + +%% + + + + diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_pre_emphasis.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_pre_emphasis.m new file mode 100644 index 0000000..525956b --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_pre_emphasis.m @@ -0,0 +1,112 @@ +database_type = 'mysql'; +dataBase = 'labor_highspeed'; +db = DBHandler("dataBase", [dataBase], "type", database_type); + + +M = 4; +fp = QueryFilter(); +fp.where('Runs', 'pam_level','EQUALS', M); +fp.where('Runs', 'fiber_length','EQUALS', 2); +fp.where('Runs', 'wavelength','EQUALS', 1310); +% fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis +fp.where('Runs', 'rop_attenuation','EQUALS', 0); + +fields = db.getTableFieldNames('power_state_info'); +fields = [fields; db.getTableFieldNames('dashboard_ungrouped_aug_nov_2025')]; %dashboard_ungrouped_after_nov_2025 dashboard_ungrouped_aug_nov_2025 +[dataTable,~] = db.queryDB(fp, fields); + +%% +cfg = struct; +cfg.x_axis = 'grossrate'; % 'symbol rate' | 'bitrate' | 'wavelength' grossrate +cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ... + +cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise +cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available +cfg.show_raw = false; +cfg.show_spread = 'none'; % 'none' or 'iqr' or minmax +cfg.agg = 'min'; % or 'median' +cfg.show_precoded = 0; +% cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional +cfg.fec_lines = []; +cfg.plot.custom_colors = [ + clr.Paired.red; + clr.Paired.blue; + clr.Paired.green; + clr.Paired.orange; + clr.Paired.purple +]; + +cfg.plot.custom_colors_scatter = [ + clr.Paired.lightred; + clr.Paired.lightblue; + clr.Paired.lightgreen; + clr.Paired.lightorange; + clr.Paired.lightpurple +]; + + +% New styling knobs +cfg.plot.use_cbrewer2 = true; +cfg.plot.colormap = 'Paired'; +cfg.plot.paired_dark_first = false; % dark for lines, light for scatter +cfg.plot.lineWidth = 2.0; +cfg.plot.errWidth = 1.2; +cfg.plot.scatterAlpha = 0.35; +cfg.plot.legendLocation = 'best'; +cfg.plot.fecLineWidth = 2.4; % thicker FEC limits +cfg.plot.lineStyle_pre_emph_on = '-'; +cfg.plot.lineStyle_pre_emph_off = '-'; + +%% +cfg.figure_number = 42; + + +% ---- VNLE, no pre-emph (solid red) ---- +cfg.plot.custom_colors = [clr.Paired.red]; +cfg.plot.custom_linetypes = {'-'}; +cfg.filters = struct('is_mpi',0,'pam_level',M, ... + 'equalizer_structure',equalizer_structure.vnle, ... + 'pre_emph',0); +plot_measurements_gpt(dataTable, cfg); + +% ---- VNLE, with pre-emph (dashed red) ---- +cfg.plot.custom_colors = [clr.Paired.red]; +cfg.plot.custom_linetypes = {'--'}; +cfg.filters.pre_emph = 1; +plot_measurements_gpt(dataTable, cfg); + +% ---- VNLE PF MLSE, no pre-emph (solid green) ---- +cfg.plot.custom_colors = [clr.Paired.green]; +cfg.plot.custom_linetypes = {'-'}; +cfg.filters.equalizer_structure = equalizer_structure.vnle_pf_mlse; +cfg.filters.pre_emph = 0; +plot_measurements_gpt(dataTable, cfg); + +% ---- VNLE PF MLSE, with pre-emph (dashed green) ---- +cfg.plot.custom_colors = [clr.Paired.green]; +cfg.plot.custom_linetypes = {'--'}; +cfg.filters.pre_emph = 1; +plot_measurements_gpt(dataTable, cfg); + + +% === FEC LINES (no legend) === +yline([2.2e-4 4.85e-3 2e-2], ... + 'LineWidth',1.5,'Color',[0.4 0.4 0.4], ... + 'LineStyle',':','HandleVisibility','off'); + + +% === BEAUTIFY === +% beautifyBERplot; % your function + + +%% === EXPORT TO TIKZ === +outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\compare_pre_emphasis.tikz'; + +matlab2tikz(outfile, ... + 'width','\fwidth', ... + 'height','\fheight', ... + 'showInfo',false, ... + 'extraAxisOptions',{ ... + 'legend style={font=\footnotesize}', ... + 'legend columns=1' ... + } ); \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/example_usage.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/example_usage.m new file mode 100644 index 0000000..bf3d4bf --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/example_usage.m @@ -0,0 +1,92 @@ +% ============================================================ +% MINIMAL EXAMPLE: Query → Analyze → Plot → Extract X/Y data +% ============================================================ + +%% === Load from database === +db = DBHandler("dataBase","labor_highspeed","type","mysql"); + +fp = QueryFilter(); +fp.where('Runs','fiber_length','EQUALS',2); +% fp.where('Runs','pam_level','EQUALS',6); % PAM-4 +% fp.where('Runs','db_mode','EQUALS',0); % w/o pre-emph + +fields = db.getTableFieldNames('dashboard_ungrouped_alltime'); +[dataTable,~] = db.queryDB(fp, fields); + + + + + +%% === Define config === + +for m = [4,6,8] + + cfg = struct; + cfg.x_axis = 'symbolrate'; + cfg.y_axis = 'Alpha'; + cfg.group_by = {'equalizer_structure','pre_emph'}; + cfg.filters = struct('is_mpi',0,'pam_level',m, ... + 'equalizer_structure',equalizer_structure.vnle_pf_mlse, ... + 'pre_emph',0); + cfg.agg = 'max'; + cfg.outlier = 'mad'; + cfg.show_raw = false; + cfg.show_precoded = 0; + + % Plot cosmetics (minimal) + cfg.plot = struct; + cfg.plot.custom_colors = linspecer(8); + cfg.plot.custom_linetypes = {'-'}; + cfg.plot.lineWidth = 2; + + + % ============================================================ + % === ANALYSIS ONLY (no plotting) ============================= + % ============================================================ + A = analyze_measurements_gpt(dataTable, cfg); + + % Now you have: + % A.raw.x = raw x-values + % A.raw.y = raw BER values + % A.group{1}.x = unique sorted x-values + % A.group{1}.y = aggregated BER for each x + + x_values = A.group{1}.x; + y_values = A.group{1}.y; + + + % ============================================================ + % === PLOT ==================================================== + % ============================================================ + + figure(10);hold on + cfg.ax = gca; % optional: plot into existing axes + plot(x_values,y_values,... + 'LineWidth', 2, ... + 'Color', clr.Set1.red, ... + 'MarkerSize', 5, ... + 'MarkerFaceColor', clr.Set1.red,... + 'Marker','o'); + % [h, ~] = plot_measurements_gpt(dataTable, cfg); + + title('Minimal VNLE BER Example') + xlabel('Grossrate [Gb/s]') + ylabel('BER') + xticks(100:30:220) + xlim([100,220]); + ylim([0,1]); + +end +% outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\alphas.tikz'; +% matlab2tikz(outfile, ... +% 'width','\fwidth', ... +% 'height','\fheight', ... +% 'showInfo',false, ... +% 'extraAxisOptions',{ ... +% 'legend style={font=\footnotesize}', ... +% 'legend columns=1' ... +% 'every axis/.append style={font=\scriptsize}',... +% 'minor grid style={line width=0.2pt, solid, color=black!10}',... +% 'grid style={line width=0.4pt, solid, color=black!20}',... +% 'grid style={dashed}',... +% }); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/generate_spectrum_plots.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/generate_spectrum_plots.m new file mode 100644 index 0000000..f90cf1a --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/generate_spectrum_plots.m @@ -0,0 +1,111 @@ +dsp_options.storage_path = 'Z:\2024\sioe_labor\'; +dsp_options.max_occurences = 1; +database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' ); + +rate = 390e9; + +%% 1 - PAM 4 with preemphasis +fp = QueryFilter(); +M = 4; +fp.where('Runs', 'pam_level','EQUALS', M); +fp.where('Runs', 'bitrate','EQUALS', rate);%360,390 +fp.where('Runs', 'fiber_length','EQUALS', 2); +fp.where('Runs', 'wavelength','EQUALS', 1310); +fp.where('Runs', 'db_mode','EQUALS', 0); +fp.where('Runs', 'rop_attenuation','EQUAL', 0); + +[dataTable,~] = db.queryDB(fp, database.getTableFieldNames('Runs')); + +dataTable = queryRunid(dataTable.run_id, database); +fsym = dataTable.symbolrate; +M = double(dataTable.pam_level); + +% Load and Sync signal data from DB +[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options); + +% Preprocess signal +Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym); + +if rate == 390e9 +Scpe_sig.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',-5.8); +elseif rate == 300e9 +Scpe_sig.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',-4.7); +end +ylim([-30,3]); +xlim([-5,100]); +Scpe_sig.spectrum("fignum",201,"normalizeTo0dB",0,"displayname",'Rx'); + + +%% 1 - PAM 4 without preemphasis +fp = QueryFilter(); +M = 4; +fp.where('Runs', 'pam_level','EQUALS', M); +fp.where('Runs', 'bitrate','EQUALS', rate);%360,390 +fp.where('Runs', 'fiber_length','EQUALS', 2); +fp.where('Runs', 'wavelength','EQUALS', 1310); +fp.where('Runs', 'db_mode','EQUALS', 1); +fp.where('Runs', 'rop_attenuation','EQUAL', 0); + +[dataTable,~] = db.queryDB(fp, database.getTableFieldNames('Runs')); + +dataTable = queryRunid(dataTable.run_id, database); +fsym = dataTable.symbolrate; +M = double(dataTable.pam_level); + +% Load and Sync signal data from DB +[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options); + +% Preprocess signal +Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym); + +Scpe_sig.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',0); +ylim([-30,3]); +xlim([-5,100]); +Scpe_sig.spectrum("fignum",201,"normalizeTo0dB",0,"displayname",'Rx'); + + + +if 1 + %% show freuqncy response of filter + + measure = 1; + + freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",70,"f_ref",256e9); + % + Digi_sig = freqresp.buildOFDM(); + + % Digi_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]); + + Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.butterworth,"active",true).process(Digi_sig); + + Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.bessel_inp,"active",true).process(Digi_sig); + + freqresp.estimate(Digi_sig,"fileName",'','save',false); + + freqresp.plot() + + a = gca; + a.YTick = [-30,-20,-10,0]; + + %% system frex + + + precomp_filename ='lab_high_speed'; + precomp_path = "C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\precomp"; + freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',92e9); + freqresp.load('loadPath', precomp_path, 'fileName', precomp_filename); + + fprintf('Plotting: %s\n', precomp_filename); + freqresp.plot(); + + outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\spectrum_2.tikz'; +matlab2tikz(outfile, ... + 'width','\fwidth', ... + 'height','\fheight', ... + 'showInfo',false, ... + 'extraAxisOptions',{ ... + 'legend style={font=\footnotesize}', ... + 'legend columns=1' ... + }); + +end \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt.m new file mode 100644 index 0000000..a31fab0 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt.m @@ -0,0 +1,222 @@ +function [h, M, cfg] = plot_measurements_gpt(T, cfg) +% PLOT_MEASUREMENTS_GPT +% Plot measurements, using analyze_measurements_gpt for data handling. +% +% Usage: +% h = plot_measurements_gpt(dataTable, cfg); +% [h, M] = plot_measurements_gpt(dataTable, cfg); +% +% For values only, without plotting, call: +% [M, cfg] = analyze_measurements_gpt(dataTable, cfg); + +if nargin < 2, cfg = struct; end + +% --- First: run analysis (filtering, grouping, aggregation) --- +[M, cfg] = analyze_measurements_gpt(T, cfg); + +nG = M.nGroups; + +%% ---- Plot defaults ---- +plotdefs = struct( ... + 'use_cbrewer2' , true, ... + 'colormap' , 'Paired', ... + 'paired_dark_first' , true, ... + 'lineWidth' , 1.8, ... + 'errWidth' , 1.0, ... + 'scatterSize' , 14, ... + 'scatterAlpha' , 0.35, ... + 'marker' , 'o', ... + 'marker_precoded' , 's', ... + 'legendLocation' , 'best', ... + 'fecLineWidth' , 2.2, ... + 'fecColor' , [0.25 0.25 0.25], ... + 'capSize' , 6, ... + 'lineStyle_default' , '-', ... + 'custom_colors' , [], ... + 'custom_colors_scatter' , [] ... +); +if ~isfield(cfg,'plot') || isempty(cfg.plot) + cfg.plot = struct; +end +cfg.plot = filldefaults(cfg.plot, plotdefs); + +%% ---- Colors ---- +[cols_line, cols_scatter] = buildGroupColors(nG, cfg.plot); + +%% ---- Axes / Figure handling ---- +if isfield(cfg,'ax') && ~isempty(cfg.ax) && isgraphics(cfg.ax,'axes') + ax = cfg.ax; + set(gcf,'CurrentAxes',ax); +else + if isfield(cfg,'figure_number') && ~isempty(cfg.figure_number) + figure(cfg.figure_number); + else + figure; + end + ax = gca; +end +hold(ax,'on'); +grid(ax,'on'); + +h.lines = gobjects(nG,1); +h.err = gobjects(nG,1); +h.scat = gobjects(nG,1); +h.lines_p = gobjects(nG,1); + +%% ---- Plot each group ---- +for gi = 1:nG + g = M.group{gi}; + xu = g.x; + yu = g.y; + ylo = g.y_lo; + yhi = g.y_hi; + + % --- Linestyle selection --- + ls = cfg.plot.lineStyle_default; + if isfield(cfg.plot,'custom_linetypes') && ~isempty(cfg.plot.custom_linetypes) + L = cfg.plot.custom_linetypes; + ls = L{ mod(gi-1, numel(L)) + 1 }; + end + + colL = cols_line(gi,:); + lbl = g.label; + + % Main line + h.lines(gi) = plot(ax, xu, yu, ... + 'LineWidth', cfg.plot.lineWidth, ... + 'Marker', cfg.plot.marker, 'MarkerSize', 3, ... + 'Color', colL, 'LineStyle', ls, ... + 'DisplayName', char(lbl)); + + % Spread + if any(isfinite(ylo)) && any(isfinite(yhi)) + h.err(gi) = errorbar(ax, xu, yu, ylo, yhi, 'LineStyle','none', ... + 'Color', colL, 'CapSize', cfg.plot.capSize, 'HandleVisibility','off'); + h.err(gi).LineWidth = cfg.plot.errWidth; + end + + % Raw scatter + if cfg.show_raw + % reuse stored raw data (no extra filtering) + xi = g.x_raw; + yi = g.y_raw; + colS = cols_scatter(gi,:); + scatter(ax, xi, yi, cfg.plot.scatterSize, colS, 'filled', ... + 'MarkerFaceAlpha', cfg.plot.scatterAlpha, ... + 'MarkerEdgeAlpha', cfg.plot.scatterAlpha, ... + 'HandleVisibility','off'); + end + + % Precoded overlay + if cfg.show_precoded && ~isempty(g.y_precoded) && strcmpi(M.y_axis,'BER') + ypu = g.y_precoded; + h.lines_p(gi) = plot(ax, xu, ypu, ... + 'LineWidth', max(1.2, cfg.plot.lineWidth-0.2), ... + 'Marker', cfg.plot.marker_precoded, 'MarkerSize', 3, ... + 'Color', colL, 'LineStyle', ':', ... + 'DisplayName', [char(lbl) ' (precoded)']); + end +end + +%% ---- Axes / Labels / FEC ---- +ylabel(ax, M.y_axis, 'Interpreter','none'); +xlabel(ax, M.x_label, 'Interpreter','none'); +set(ax, 'YScale', cfg.y_scale, 'FontSize', 11); + +% X ticks/limits using all group x-values +allX = cellfun(@(g) g.x(:), M.group, 'UniformOutput', false); +allX = unique(vertcat(allX{:})); +if ~isempty(allX) + xticks(ax, allX); + xticklabels(cellstr(num2str(round(allX,1), '%.4f'))) + xlim(ax, [min(allX), max(allX)]); +end + +if startsWith(M.y_axis,"BER",'IgnoreCase',true) + for v = cfg.fec_lines + yline(ax, v, '--', 'Color', cfg.plot.fecColor, ... + 'LineWidth', cfg.plot.fecLineWidth, 'HandleVisibility','off'); + end + ylim(ax, [1e-5, 0.5]); + yticks(ax, [1e-5, 1e-4, 1e-3, 1e-2, 1e-1]); +end + +% legend(ax, 'Location', cfg.plot.legendLocation); % if you want legends + +box(ax,'on'); + +end % ===== main ===== + + +%% ===================== Helpers ===================== + +function cfg = filldefaults(cfg, defs) +fn = fieldnames(defs); +for i = 1:numel(fn) + f = fn{i}; + if ~isfield(cfg, f) || isempty(cfg.(f)) + cfg.(f) = defs.(f); + elseif isstruct(defs.(f)) && isstruct(cfg.(f)) + cfg.(f) = filldefaults(cfg.(f), defs.(f)); + end +end +end + +function [cols_line, cols_scatter] = buildGroupColors(nG, plotcfg) + +% 1) User-provided custom colors +if isfield(plotcfg,'custom_colors') && ~isempty(plotcfg.custom_colors) + C = plotcfg.custom_colors; + if size(C,1) < nG + error('custom_colors must have at least nG=%d rows.', nG); + end + cols_line = C(1:nG, :); + + if isfield(plotcfg,'custom_colors_scatter') && ~isempty(plotcfg.custom_colors_scatter) + Cs = plotcfg.custom_colors_scatter; + if size(Cs,1) < nG + error('custom_colors_scatter must have at least nG=%d rows.', nG); + end + cols_scatter = Cs(1:nG, :); + else + cols_scatter = zeros(nG,3); + for i = 1:nG + cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.40); + end + end + return; +end + +% 2) Standard behavior +useBrewer = plotcfg.use_cbrewer2 && exist('cbrewer2','file')==2; +if useBrewer + N = max(2*nG, 12); + C = cbrewer2(plotcfg.colormap, N); + cols_line = zeros(nG,3); + cols_scatter = zeros(nG,3); + for i = 1:nG + if plotcfg.paired_dark_first + dark = C(2*i-1, :); + light = C(2*i, :); + else + light = C(2*i-1, :); + dark = C(2*i, :); + end + cols_line(i,:) = dark; + cols_scatter(i,:) = light; + end +else + C = lines(max(nG,7)); + cols_line = C(1:nG,:); + cols_scatter = zeros(nG,3); + for i = 1:nG + cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.50); + end +end + +end + +function c2 = lightenColor(c, fracTowardWhite) +c = c(:).'; +c2 = (1-fracTowardWhite)*c + fracTowardWhite*1; +end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt_old.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt_old.m new file mode 100644 index 0000000..207b4d1 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt_old.m @@ -0,0 +1,436 @@ +function h = plot_measurements_gpt_old(T, cfg) +% Versatile plotting from your DB table (with cbrewer2 'Paired' palette). +% +% Usage: +% h = plot_measurements_flex(dataTable, cfg) + +%% ---- Defaults +if nargin < 2, cfg = struct; end +defaults = struct( ... + 'x_axis' , 'symbolrate', ... + 'y_axis' , 'BER', ... + 'y_scale' , 'auto', ... + 'group_by' , {{'equalizer_structure','pre_emph'}}, ... + 'filters' , struct, ... + 'agg' , 'mean', ... + 'outlier' , 'auto', ... + 'mad_z' , 4, ... + 'pct_limits' , [2.5 97.5], ... + 'min_pts_x' , 3, ... + 'show_raw' , true, ... + 'show_precoded', [], ... + 'show_spread' , 'none', ... + 'fec_lines' , [], ... + 'plot', struct() ... +); +cfg = filldefaults(cfg, defaults); + +% ---- Plot defaults (new) +plotdefs = struct( ... + 'use_cbrewer2' , true, ... + 'colormap' , 'Paired', ... % ColorBrewer 'Paired' + 'paired_dark_first' , true, ... % dark for lines, light for scatter + 'lineWidth' , 1.8, ... + 'errWidth' , 1.0, ... + 'scatterSize' , 14, ... + 'scatterAlpha' , 0.35, ... + 'marker' , 'o', ... + 'marker_precoded' , 's', ... + 'lineStyle_pre_emph_on' , '--', ... + 'lineStyle_pre_emph_off', '-', ... + 'legendLocation' , 'best', ... + 'fecLineWidth' , 2.2, ... % thicker FEC limits + 'fecColor' , [0.25 0.25 0.25], ... + 'capSize' , 6, ... + 'lineStyle_default' , '-', ... + 'use_pre_emph_styling' , true ... +); +cfg.plot = filldefaults(cfg.plot, plotdefs); + +%% ---- Derived/prep columns +if ~ismember('pre_emph', T.Properties.VariableNames) + if ~ismember('db_mode', T.Properties.VariableNames) + error('Missing column "db_mode" for pre_emph derivation.'); + end + T.pre_emph = T.db_mode == 0; +end +if ~ismember(cfg.y_axis, T.Properties.VariableNames) + error('y_axis "%s" not found in table.', cfg.y_axis); +end + +isBER = startsWith(cfg.y_axis, "BER", 'IgnoreCase', true); +if strcmpi(cfg.y_scale,'auto'), cfg.y_scale = tern(isBER, 'log', 'linear'); end +if strcmpi(cfg.outlier,'auto'), cfg.outlier = tern(isBER, 'mad', 'none'); end +if isempty(cfg.show_precoded) + cfg.show_precoded = isBER && ismember('BER_precoded', T.Properties.VariableNames); +end + +%% ---- Filters +T = applyFilters(T, cfg.filters); +[x_raw, x_label] = computeX(T, cfg.x_axis); +y_raw = T.(cfg.y_axis); + +validXY = isfinite(x_raw) & isfinite(y_raw); +T = T(validXY, :); +x_raw = x_raw(validXY); +y_raw = y_raw(validXY); + +if mean(abs(y_raw)) > 1e8 + %giga values + y_raw = y_raw.*1e-9; +end + +if cfg.show_precoded && ismember('BER_precoded', T.Properties.VariableNames) + y_raw_p = T.BER_precoded(validXY); +else + y_raw_p = []; +end + +%% ---- Grouping +group_by = cfg.group_by; +if ~all(ismember(group_by, T.Properties.VariableNames)) + error('Some group_by columns are missing in table.'); +end +[G, grpTbl] = findgroups(T(:, group_by)); +nG = max(G); + +% ==== Colors (cbrewer2 'Paired' with dark/ light pairs) ==== +[cols_line, cols_scatter] = buildGroupColors(nG, cfg.plot); + +%% ---- Axes / Figure handling (new unified logic) + +% Priority: +% 1) cfg.ax → use existing axes (subplots/tiles) +% 2) cfg.figure_number → select/create figure +% 3) fallback: create new figure + +if isfield(cfg,'ax') && ~isempty(cfg.ax) && isgraphics(cfg.ax,'axes') + ax = cfg.ax; % use caller-provided axes + set(gcf,'CurrentAxes',ax); +else + if isfield(cfg,'figure_number') && ~isempty(cfg.figure_number) + figure(cfg.figure_number); + else + figure; + end + ax = gca; % active axes +end + +hold(ax,'on'); +grid(ax,'on'); + + +h.lines = gobjects(nG,1); +h.err = gobjects(nG,1); +h.scat = gobjects(nG,1); +h.lines_p = gobjects(nG,1); + +for gi = 1:nG + idx = (G==gi); + Ti = T(idx,:); + xi = x_raw(idx); + yi = y_raw(idx); + + % Aggregate per unique x + [xu, ia, iu] = unique(xi); + yu = nan(size(xu)); + ylo = nan(size(xu)); + yhi = nan(size(xu)); + + for k = 1:numel(xu) + bin = (iu==k); + yy = yi(bin); + yy = yy(isfinite(yy)); + if isempty(yy), continue; end + km = outlierMask(yy, cfg, strcmpi(cfg.y_scale,'log')); + if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end + yy = yy(km); + + if strcmpi(cfg.agg,'median'), yu(k)=median(yy,'omitnan'); elseif strcmpi(cfg.agg,'mean'), yu(k)=mean(yy,'omitnan'); elseif strcmpi(cfg.agg,'min'), yu(k)=min(yy); elseif strcmpi(cfg.agg,'max'), yu(k)=max(yy); end + if strcmpi(cfg.show_spread,'iqr') + q = prctile(yy,[25 75]); + ylo(k) = max(yu(k)-q(1), eps); + yhi(k) = max(q(2)-yu(k), eps); + elseif strcmpi(cfg.show_spread,'minmax') + ylo(k) = min(yy); + yhi(k) = max(yy); + end + end + + % sort + [xu, ord] = sort(xu); + yu = yu(ord); + ylo = ylo(ord); + yhi = yhi(ord); + + % Styles + % Decide if we style by pre_emph + % --- LINE TYPE SELECTION (no pre-emphasis logic) --- + ls = cfg.plot.lineStyle_default; + + % User-defined override (cycled) + if isfield(cfg.plot,'custom_linetypes') && ~isempty(cfg.plot.custom_linetypes) + L = cfg.plot.custom_linetypes; + ls = L{ mod(gi-1, numel(L)) + 1 }; + end + + lbl = buildLabel(grpTbl(gi,:), group_by); + + % Main line (dark) + colL = cols_line(gi,:); + h.lines(gi) = plot(xu, yu, ... + 'LineWidth', cfg.plot.lineWidth, ... + 'Marker', cfg.plot.marker, 'MarkerSize', 3, ... + 'Color', colL, 'LineStyle', ls, ... + 'DisplayName', char(lbl)); + + % Spread (IQR) in line color + if any(isfinite(ylo)) && any(isfinite(yhi)) + h.err(gi) = errorbar(xu, yu, ylo, yhi, 'LineStyle','none', ... + 'Color', colL, 'CapSize', cfg.plot.capSize, 'HandleVisibility','off'); + h.err(gi).LineWidth = cfg.plot.errWidth; + end + + % Raw kept scatter (light) + if cfg.show_raw + keep_all = false(size(yi)); + for k = 1:numel(xu) + bin = (iu==k); + yy = yi(bin); + km = outlierMask(yy, cfg, strcmpi(cfg.y_scale,'log')); + if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end + keep_all(bin) = km; + end + colS = cols_scatter(gi,:); + scatter(xi(keep_all), yi(keep_all), cfg.plot.scatterSize, colS, 'filled', ... + 'MarkerFaceAlpha', cfg.plot.scatterAlpha, 'MarkerEdgeAlpha', cfg.plot.scatterAlpha, ... + 'HandleVisibility','off'); + end + + % Precoded overlay (dotted, squares), in line color + if cfg.show_precoded && ~isempty(y_raw_p) && strcmpi(cfg.y_axis,'BER') + ypi = y_raw_p(idx); + ypu = nan(size(xu)); + for k = 1:numel(xu) + bin = (iu==k); + yy = ypi(bin); + yy = yy(isfinite(yy)); + if isempty(yy), continue; end + km = outlierMask(yy, cfg, true); + if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end + yy = yy(km); + if strcmpi(cfg.agg,'median'), ypu(k)=median(yy,'omitnan'); elseif strcmpi(cfg.agg,'mean'), ypu(k)=mean(yy,'omitnan'); elseif strcmpi(cfg.agg,'min'), ypu(k)=min(yy); elseif strcmpi(cfg.agg,'max'), ypu(k)=max(yy); end + end + h.lines_p(gi) = plot(xu, ypu, ... + 'LineWidth', max(1.2, cfg.plot.lineWidth-0.2), ... + 'Marker', cfg.plot.marker_precoded, 'MarkerSize', 3, ... + 'Color', colL, 'LineStyle', ':', ... + 'DisplayName', [char(lbl) ' (precoded)']); + end + + +end + +%% ---- Axes / Labels / FEC +ylabel(cfg.y_axis, 'Interpreter','none'); +xlabel(x_label, 'Interpreter','none'); +set(gca, 'YScale', cfg.y_scale, 'FontSize', 11); +% legend('Location', cfg.plot.legendLocation); box on; + +xticks(floor(xu)); +xlim([min(xu), max(xu)]) + +if startsWith(cfg.y_axis,"BER",'IgnoreCase',true) + for v = cfg.fec_lines + yline(v, '--', 'Color', cfg.plot.fecColor, ... + 'LineWidth', cfg.plot.fecLineWidth, 'HandleVisibility','off'); + end + ylim([1e-5, 0.5]); + yticks([1e-5, 1e-4, 1e-3, 1e-2, 1e-1]); +end + + + +end % ===== main ===== + + +%% ===================== Helpers ===================== + +function cfg = filldefaults(cfg, defs) +fn = fieldnames(defs); +for i = 1:numel(fn) + f = fn{i}; + if ~isfield(cfg, f) || isempty(cfg.(f)) + cfg.(f) = defs.(f); + elseif isstruct(defs.(f)) && isstruct(cfg.(f)) + cfg.(f) = filldefaults(cfg.(f), defs.(f)); % recursive for structs + end +end +end + +function out = tern(cond, a, b) + if cond + out = a; + else + out = b; + end +end + +function T2 = applyFilters(T, filters) +if isempty(filters), T2 = T; return; end +keep = true(height(T),1); +fns = fieldnames(filters); +for i = 1:numel(fns) + name = fns{i}; + if ~ismember(name, T.Properties.VariableNames) + warning('Filter column "%s" not found. Ignored.', name); %#ok<*WNTAG> + continue + end + val = filters.(name); + col = T.(name); + if isa(val,'function_handle') + m = val(col); + if ~islogical(m) || ~isequal(size(m), size(col)) + error('Filter for %s must return logical mask of same size.', name); + end + keep = keep & m; + else + keep = keep & ismember(col, val); + end +end +T2 = T(keep,:); +end + +function [x, label] = computeX(T, whichX) +switch lower(whichX) + case {'symbolrate','baudrate'} + x = T.symbolrate * 1e-9; + label = 'Symbol rate [GBd]'; + case 'bitrate' + if ~ismember('pam_level', T.Properties.VariableNames) + error('bitrate requires "pam_level" column.'); + end + bits = floor(log2(double(T.pam_level))*10)/10; + x = (T.symbolrate .* bits) * 1e-9; + label = 'Grossrate [Gb/s]'; + case 'grossrate' + x = (T.grossrate) * 1e-9; + label = 'Grossrate [Gb/s]'; + otherwise + if ~ismember(whichX, T.Properties.VariableNames) + error('x_axis "%s" not found in table.', whichX); + end + x = T.(whichX); + label = whichX; +end +x = double(x(:)); +end + +function keep = outlierMask(y, cfg, useLog) +if isempty(y), keep = false(size(y)); return; end +y = y(:); +switch lower(cfg.outlier) + case 'none' + keep = true(size(y)); return + case 'mad' + z = tern(useLog, log10(y), y); + med = median(z,'omitnan'); + madv = median(abs(z-med),'omitnan'); + if ~(isfinite(madv) && madv>0) + keep = true(size(y)); return + end + sigma = 1.4826*madv; + zz = tern(useLog, log10(y), y); + keep = abs(zz - med) <= cfg.mad_z*sigma; + case 'pctl' + pr = prctile(y, cfg.pct_limits); + keep = (y >= pr(1)) & (y <= pr(2)); + otherwise + error('Unknown outlier mode "%s".', cfg.outlier); +end +end + +function s = buildLabel(grpRow, group_by) +parts = strings(1, numel(group_by)); +for i = 1:numel(group_by) + key = group_by{i}; + val = grpRow.(key); + if iscell(val), val = val{1}; end + if islogical(val), val = tern(val,'w/','w/o'); end + if key == "equalizer_structure" + key = ''; + val = upper(val); + val = strrep(val,'_',' '); + end + + if key == "pre_emph" + % key = strrep(key,'_','-'); + val = [val, ' pre-emph.']; + key = ''; + end + + parts(i) = sprintf('%s %s', key, string(val)); +end +s = strjoin(parts, ', '); +end + +function [cols_line, cols_scatter] = buildGroupColors(nG, plotcfg) + +% --- 1) User-provided custom colors ------------------------------- +if isfield(plotcfg,'custom_colors') && ~isempty(plotcfg.custom_colors) + C = plotcfg.custom_colors; + if size(C,1) < nG + error('custom_colors must have at least nG=%d rows.', nG); + end + cols_line = C(1:nG, :); + + % Scatter colors: either user-provided or lightened + if isfield(plotcfg,'custom_colors_scatter') && ~isempty(plotcfg.custom_colors_scatter) + Cs = plotcfg.custom_colors_scatter; + if size(Cs,1) < nG + error('custom_colors_scatter must have at least nG=%d rows.', nG); + end + cols_scatter = Cs(1:nG, :); + else + % auto-lighten scatter colors + cols_scatter = zeros(nG,3); + for i = 1:nG + cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.40); + end + end + return; +end + +% --- 2) Standard behavior (using cbrewer2 or fallback) ------------ +useBrewer = plotcfg.use_cbrewer2 && exist('cbrewer2','file')==2; +if useBrewer + N = max(2*nG, 12); + C = cbrewer2(plotcfg.colormap, N); + cols_line = zeros(nG,3); + cols_scatter = zeros(nG,3); + for i = 1:nG + if plotcfg.paired_dark_first + dark = C(2*i-1, :); + light = C(2*i, :); + else + light = C(2*i-1, :); + dark = C(2*i, :); + end + cols_line(i,:) = dark; + cols_scatter(i,:) = light; + end +else + C = lines(max(nG,7)); + cols_line = C(1:nG,:); + cols_scatter = zeros(nG,3); + for i = 1:nG + cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.50); + end +end + +end + +function c2 = lightenColor(c, fracTowardWhite) +c = c(:).'; +c2 = (1-fracTowardWhite)*c + fracTowardWhite*1; +end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/rates.csv b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/rates.csv new file mode 100644 index 0000000..c00999e --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/rates.csv @@ -0,0 +1,38 @@ +X-Werte;PAM-2;PAM-4;PAM-6;PAM-8;PAM-12 +224;204;;;; +205,5;193,4;;;; +205,1;189,7;;;; +192;168;;;; +240;;427,4;;; +225;;420,5;;; +210;;336;;; +184;;332;;; +192;;320;;; +176;;306,0869565;;; +190;;304;;; +168;;294;;; +156,2;;287,1;;; +160,8;;286,9;;; +170;;272;;; +132;;250,9505703;;; +112;;209,3457944;;; +172;;337;;; +216;;;474,6;; +147,2;;;329,9;; +132;;;319,7891753;; +143,1;;;318;; +160;;;377;; +225;;;;562,5; +200;;;;510; +160;;;;438; +180;;;;432; +180;;;;432; +144;;;;384; +143,7;;;;363,4; +144;;;;360; +136;;;;353,859497; +136;;;;342,7995295; +128;;;;329,0488432; +129,7;;;;311,2; +160;;;;413; +160;;;;;481,2 diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/run_plot_measurements.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/run_plot_measurements.m new file mode 100644 index 0000000..6ebf0fc --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/run_plot_measurements.m @@ -0,0 +1,76 @@ +database_type = 'mysql'; +dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db'; +db = DBHandler("dataBase", [dataBase], "type", database_type); + +M = 8; +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 987); +fp.where('Runs', 'pam_level','EQUALS', M); +% fp.where('Runs', 'symbolrate','EQUALS', 165e9); %150, 165, 180, 195, 210, 225, 240 +fp.where('Runs', 'fiber_length','EQUALS', 2); +% fp.where('Runs', 'is_mpi','EQUALS', 0); +% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); +% fp.where('Runs', 'interference_path_length','EQUALS', 1000); +% fp.where('Runs', 'loop_id','GREATER_THAN', 11); +% fp.where('Runs', 'sir','EQUALS',18); +fp.where('Runs', 'wavelength','EQUALS', 1310); +% fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis +% fp.where('Runs', 'rop_attenuation','EQUALS', 0); + +fields = db.getTableFieldNames('power_state_info'); +fields = [fields; db.getTableFieldNames('dashboard_ungrouped_alltime')]; %dashboard_ungrouped_after_nov_2025 dashboard_ungrouped_aug_nov_2025 +[dataTable,~] = db.queryDB(fp, fields); + + +%% +cfg = struct; +cfg.x_axis = 'grossrate'; % 'symbol rate' | 'bitrate' | 'wavelength' grossrate +cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ... +cfg.group_by = {'equalizer_structure','pre_emph'}; +cfg.filters = struct('is_mpi',0,'pam_level',M,'equalizer_structure',[equalizer_structure.ml_mlse]);%,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle]); + +cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise +cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available +cfg.show_raw = false; +cfg.show_spread = 'none'; % 'none' or 'iqr' or minmax +cfg.agg = 'min'; % or 'median' +cfg.show_precoded = 0; +cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional + +cfg.figure_number = 42; +cfg.plot.custom_colors = [ + clr.Paired.red; + clr.Paired.blue; + clr.Paired.green; + clr.Paired.orange; + clr.Paired.purple +]; + +cfg.plot.custom_colors_scatter = [ + clr.Paired.lightred; + clr.Paired.lightblue; + clr.Paired.lightgreen; + clr.Paired.lightorange; + clr.Paired.lightpurple +]; + + +% New styling knobs +cfg.plot.use_cbrewer2 = true; +cfg.plot.colormap = 'Paired'; +cfg.plot.paired_dark_first = false; % dark for lines, light for scatter +cfg.plot.lineWidth = 2.0; +cfg.plot.errWidth = 1.2; +cfg.plot.scatterAlpha = 0.35; +cfg.plot.legendLocation = 'best'; +cfg.plot.fecLineWidth = 2.4; % thicker FEC limits + +plot_measurements_gpt(dataTable, cfg); + +% beautifyBERplot() + +%% FIG PRE EMPHASIS + + + + diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/gmi_vs_rate.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/gmi_vs_rate.m index 7f84f62..26aaddb 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/gmi_vs_rate.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/gmi_vs_rate.m @@ -17,7 +17,9 @@ fp.where('Runs', 'wavelength','EQUALS', 1310); % fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis fp.where('Runs', 'rop_attenuation','EQUALS', 0); -[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard')); +fields = db.getTableFieldNames('power_state_info'); +fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')]; +[dataTable,~] = db.queryDB(fp, fields); eqstructures = unique(dataTable.equalizer_structure); @@ -42,12 +44,13 @@ for pre_emph = [0,1] eq_filtered = eq_filtered(eq_filtered.DIR == "1",:); end symbolrate_sorted = sortrows(eq_filtered,{'symbolrate'}, 'ascend'); + % Example data (replace these with your real vectors) symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud bitrate = symbolrate * 2; - gmi = symbolrate_sorted.max_GMI; % BER - snr = symbolrate_sorted.max_SNR; % BER + gmi = symbolrate_sorted.GMI; % BER + snr = symbolrate_sorted.SNR; % BER cols = cbrewer2('Paired',12); dname = [char(eq_choice)]; 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 2a82dc6..8f96ba2 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m @@ -1,6 +1,6 @@ % === SETTINGS === dsp_options.append_to_db = 1; -dsp_options.max_occurences = 15; +dsp_options.max_occurences = 1; experiment = "highspeed_2024"; dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' @@ -39,26 +39,26 @@ end % === Get Run ID's === fp = QueryFilter(); -% fp.where('Runs', 'run_id','EQUALS', 987); +% fp.where('Runs', 'run_id','EQUALS', 2776); M = 6; -% fp.where('Runs', 'pam_level','EQUALS', M); -% fp.where('Runs', 'bitrate','EQUALS', 480e9); -% fp.where('Runs', 'symbolrate','EQUALS', 162e9); -fp.where('Runs', 'fiber_length','EQUALS', 1); +fp.where('Runs', 'pam_level','EQUALS', M); +% fp.where('Runs', 'bitrate','EQUALS', 390e9);%360,390 +% fp.where('Runs', 'symbolrate','EQUALS', 195e9); +fp.where('Runs', 'fiber_length','EQUALS', 10); fp.where('Runs', 'is_mpi','EQUALS', 0); % fp.where('Runs', 'interference_path_length','EQUALS', 1000); % fp.where('Runs', 'loop_id','GREATER_THAN', 11); % fp.where('Runs', 'sir','EQUALS',18); -fp.where('Runs', 'wavelength','LESS_THAN', 1311); -% fp.where('Runs', 'db_mode','EQUALS', 0); -% fp.where('Runs', 'rop_attenuation','NOT_EQUAL', 0); -% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); +% fp.where('Runs', 'wavelength','EQUALS', 1310); +fp.where('Runs', 'db_mode','EQUALS', 0); +fp.where('Runs', 'rop_attenuation','EQUAL', 0); +% fp.where('Runs', 'power_pd_in','LESS_THAN', 7); [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); % === Set LOOPS & Initialize DataStorage === dsp_options.parameters = struct(); -% dsp_options.parameters.pf_ncoeffs = [1,2];%[0,logspace(-4,0,10)]; +% dsp_options.parameters.pf_ncoeffs = [1,2];%s[0,logspace(-4,0,10)]; wh = DataStorage(dsp_options.parameters); wh.addStorage("ffe_package"); @@ -66,106 +66,136 @@ wh.addStorage("mlse_package"); wh.addStorage("vnle_package"); wh.addStorage("dbtgt_package"); wh.addStorage("dbenc_package"); +wh.addStorage("mlmlse_package"); -% === RUN IT === +%% === RUN IT === [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); -results_db = results(dataTable.db_mode==1); -results_nodb = results(dataTable.db_mode==0); -for i = 1:numel(results_db) - % VNLE (from results_nodb) - gmi_v = cellfun(@(c) c.metrics.GMI, results_nodb{1,i}.vnle_package); - ber_v = cellfun(@(c) c.metrics.BER, results_nodb{1,i}.vnle_package); - air_v = cellfun(@(c) c.metrics.AIR, results_nodb{1,i}.vnle_package); - snr_v = cellfun(@(c) c.metrics.SNR, results_nodb{1,i}.vnle_package); - [BER_VNLE(i), idx_ber] = min(ber_v); - GMI_VNLE(i) = gmi_v(idx_ber); - AIR_VNLE(i) = air_v(idx_ber); - SNR_VNLE(i) = max(snr_v); - idx_gmi_min_vnle(i) = find(gmi_v == min(gmi_v), 1); - idx_air_max_vnle(i) = find(air_v == max(air_v), 1); +%% ========================================================================= +% LOAD METADATA +% ========================================================================= +[dataTable, ~] = db.queryDB(fp, db.getTableFieldNames('Runs')); +results = results(:).'; % ensure row vector +N = numel(results); - % MLSE (from results_db) - gmi_m = cellfun(@(c) c.metrics.GMI, results_nodb{1,i}.mlse_package); - ber_m = cellfun(@(c) c.metrics.BER, results_nodb{1,i}.mlse_package); - air_m = cellfun(@(c) c.metrics.AIR, results_nodb{1,i}.mlse_package); - [BER_MLSE(i), idx_ber] = min(ber_m); - GMI_MLSE(i) = gmi_m(idx_ber); - AIR_MLSE(i) = air_m(idx_ber); - idx_gmi_min_mlse(i) = find(gmi_m == min(gmi_m), 1); - idx_air_max_mlse(i) = find(air_m == max(air_m), 1); +% ========================================================================= +% PREALLOCATE METRIC ARRAYS +% ========================================================================= +BER_VNLE = nan(1,N); +BER_MLSE = nan(1,N); +BER_DB = nan(1,N); +BER_DB_PREC = nan(1,N); +BER_MLMLSE = nan(1,N); +BER_MLMLSE_PREC = nan(1,N); - % DB (from results_db, BER_precoded) - gmi_db = cellfun(@(c) c.metrics.GMI, results_db{1,i}.dbtgt_package); - ber_db = cellfun(@(c) c.metrics.BER, results_db{1,i}.dbtgt_package); - ber_db_prec = cellfun(@(c) c.metrics.BER_precoded, results_db{1,i}.dbtgt_package); - air_db = cellfun(@(c) c.metrics.AIR, results_db{1,i}.dbtgt_package); - [BER_DB(i), idx_ber] = min(ber_db); - [BER_DB_PREC(i), idx_ber] = min(ber_db_prec); +% ========================================================================= +% EXTRACT METRICS (ONE LOOP, ROBUST) +% ========================================================================= +for i = 1:N + r = results{i}; - GMI_DB(i) = gmi_db(idx_ber); - AIR_DB(i) = air_db(idx_ber); - idx_gmi_min_db(i) = find(gmi_db == min(gmi_db), 1); - idx_air_max_db(i) = find(air_db == max(air_db), 1); + % ---- VNLE (no-DB mode) ---- + if isfield(r, 'vnle_package') && ~isempty(r.vnle_package) + pkg = r.vnle_package; + BER_VNLE(i) = min(cellfun(@(c) c.metrics.BER, pkg)); + end - % metadata - bitrate(i) = dataTable.bitrate(i); - baudrate(i) = dataTable.symbolrate(i); + % ---- Classical MLSE (DB mode) ---- + if isfield(r, 'mlse_package') && ~isempty(r.mlse_package) + pkg = r.mlse_package; + BER_MLSE(i) = min(cellfun(@(c) c.metrics.BER, pkg)); + BER_MLSE_PREC(i) = min(cellfun(@(c) c.metrics.BER_precoded, pkg)); + end + + % ---- DB Target (DB mode) ---- + if isfield(r, 'dbtgt_package') && ~isempty(r.dbtgt_package) + pkg = r.dbtgt_package; + BER_DB(i) = min(cellfun(@(c) c.metrics.BER, pkg)); + BER_DB_PREC(i) = min(cellfun(@(c) c.metrics.BER_precoded, pkg)); + end + + % ---- ML-based MLSE (both modes) ---- + if isfield(r, 'mlmlse_package') && ~isempty(r.mlmlse_package) + pkg = r.mlmlse_package; + + % raw BER + BER_MLMLSE(i) = min(cellfun(@(c) c.metrics.BER, pkg)); + + % precoded BER + if isfield(pkg{1}.metrics, 'BER_precoded') + BER_MLMLSE_PREC(i) = min(cellfun(@(c) c.metrics.BER_precoded, pkg)); + end + end end -STYLE_BASE = 2; % adjust this single number to scale markers & lines -MARKER_SIZE = STYLE_BASE; % marker size (MATLAB MarkerSize) -LINE_WIDTH = max(2, STYLE_BASE/3); % line width (keeps lines reasonable when STYLE_BASE large) +%% ========================================================================= +% METADATA (ALWAYS INDEX-ALIGNED WITH RESULTS) +% ========================================================================= +bitrate = dataTable.bitrate(:).'; +baudrate = dataTable.symbolrate(:).'; -% --- color map / method -> color assignment (keeps colors consistent) --- -cols = cbrewer2('Paired',8); -cols = linspecer(6); -d = 0; -cm.VNLE = cols(1 + d, :); -cm.MLSE = cols(2 + d, :); -cm.DB_precode = cols(3 + d, :); -cm.DB = cols(4 + d, :); % duobinary +rop_atten = dataTable.rop_attenuation(2:2:end).'; +rop_pre = dataTable.power_rop(1:2:end).'; +rop = dataTable.power_rop(2:2:end).'; -% prepare x values in GBd -xGHz = baudrate .* 1e-9; -xticks_vals = xGHz; -xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false); +% ========================================================================= +% PLOT STYLE +% ========================================================================= +STYLE_BASE = 2; +MARKER_SIZE = STYLE_BASE; +LINE_WIDTH = max(2, STYLE_BASE/3); -% common marker settings (filled, same face+edge color) -mk.VNLE = {'Marker','o','MarkerFaceColor',cm.VNLE,'MarkerEdgeColor',cm.VNLE,'MarkerSize',MARKER_SIZE}; -mk.MLSE = {'Marker','*','MarkerFaceColor',cm.MLSE,'MarkerEdgeColor',cm.MLSE,'MarkerSize',MARKER_SIZE}; -mk.DB_precode = {'Marker','^','MarkerFaceColor',cm.DB_precode,'MarkerEdgeColor',cm.DB_precode,'MarkerSize',MARKER_SIZE}; -mk.DB = {'Marker','d','MarkerFaceColor',cm.DB,'MarkerEdgeColor',cm.DB,'MarkerSize',MARKER_SIZE}; +cols = cbrewer2('Paired', 8); +cm.VNLE = cols(1,:); +cm.MLSE = cols(2,:); +cm.DB_PREC = cols(3,:); +cm.DB = cols(4,:); +cm.ML_MLSE = cols(6,:); -% ---------------- FIGURE : BER ---------------- +mk = @(col,shape) {'Marker',shape,'MarkerFaceColor',col,'MarkerEdgeColor',col,'MarkerSize',MARKER_SIZE}; + +% ========================================================================= +% FIGURE 1: BER vs BAUDRATE +% ========================================================================= figure(112+M); clf; hold on; -plot(xGHz, BER_VNLE, ... - 'DisplayName','VNLE', ... - mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); -plot(xGHz, BER_MLSE, ... - 'DisplayName','MLSE', ... - mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); -plot(xGHz, BER_DB, ... - 'DisplayName','DB tgt.', ... - mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); -plot(xGHz, BER_DB_PREC, ... - 'DisplayName','Diff. Precode + DB tgt.', ... - mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB); +xGHz = baudrate * 1e-9; + +plot(xGHz, BER_VNLE, 'DisplayName','VNLE', 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(xGHz, BER_MLSE, 'DisplayName','MLSE', 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +% plot(xGHz, BER_MLSE_PREC, 'DisplayName','MLSE', 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +plot(xGHz, BER_DB_PREC, 'DisplayName','Diff. Precode + DB', 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB_PREC); +plot(xGHz, BER_MLMLSE_PREC, 'DisplayName','ML-based MLSE', 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.ML_MLSE); + +yline(2e-2,'LineWidth',1,'HandleVisibility','off'); yline(4.85e-3,'LineWidth',1,'HandleVisibility','off'); yline(2.2e-4,'LineWidth',1,'HandleVisibility','off'); + xlabel('Baudrate in GBd'); ylabel('BER'); -set(gca, 'yscale', 'log'); -set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); -grid on; -legend('Location','best'); +set(gca, 'YScale', 'log'); grid on; legend('Location','best'); +% beautifyBERplot; -% ---------------- FIGURE 15 : GMI ---------------- + + + + + + + + + + + + + + + +%% ---------------- FIGURE 15 : GMI ---------------- figure(113+M); clf; hold on; plot(xGHz, GMI_VNLE, ... 'DisplayName','VNLE', ... diff --git a/projects/HighSpeedExperiment_2024/a_minimal_example.m b/projects/HighSpeedExperiment_2024/a_minimal_example.m new file mode 100644 index 0000000..1c45d75 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/a_minimal_example.m @@ -0,0 +1,116 @@ + + +dsp_options.storage_path = 'Z:\2024\sioe_labor\'; +dsp_options.max_occurences = 1; +db = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' ); + +fp = QueryFilter(); + +fp.where('Runs','fiber_length','EQUALS', 2); +fp.where('Runs','wavelength','EQUALS', 1310); +fp.where('Runs','bitrate','EQUALS', 300e9); +fp.where('Runs','pam_level','EQUALS', 4); +fp.where('Runs','rop_attenuation','EQUALS', 0); +fp.where('Runs','is_mpi','EQUALS', 0); +fp.where('Runs', 'db_mode','EQUALS', 0); +fields = db.getTableFieldNames('Runs'); +[dataTable,~] = db.queryDB(fp, fields); + + +fsym = dataTable.symbolrate; +M = double(dataTable.pam_level); +duob_mode = db_mode(strrep(dataTable.db_mode,'"','')); + + +% Load and Sync signal data from DB +[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options); + +% Preprocess signal +Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym); + +% Show spectrum +Scpe_sig.spectrum("fignum",1,"displayname",'Rx') + + + + +%% simple FFE + +mu_ffe = [0.0001, 0.0008, 0.001]; +mu_dfe = 0.0004; +ffe_order = [50, 0, 0]; +eq_dfe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",4096,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); + +ffe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... + "precode_mode",duob_mode,... + 'showAnalysis',0,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + + +ffe_results.metrics.print("description",'FFE'); +ffe_results.config.equalizer_structure = "ffe"; + +%% a) VNLE // b) concatenated VNLE + MLSE + +pf_ncoeffs = 1; +ffe_order = [50, 5, 5]; +dfe_order = [0,0,0]; +eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",4096,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); +pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + + + +if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation + trellexlusion = 1; +else + trellexlusion = 0; +end + +%state_mode 3 -> stat lvl; state_mode 2 -> use target lvls +%scale_mode 2 -> mmse adaption + +mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',2); + +[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode,... + 'showAnalysis', 0, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + +vnle_results.metrics.print("description",'VNLE'); +mlse_results.metrics.print("description",'VNLE + PF + MLSE'); + + +%% Duobinary Equalization + + +if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation + trellexlusion = 1; +else + trellexlusion = 0; +end +mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',3); + +ffe_order = [50, 5, 5]; +dfe_order = [0,0,0]; +eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",4096,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1); + +dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, ... + 'showAnalysis', 0,... + "postFFE", []); + +dbt_results.metrics.print("description",'Duobinary EQ'); + +%% Ml based Viterbi + +%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",length(Scpe_sig),"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ... + "traceback_depth",128,"L",1,"delta",4,"adaptive_mu",0); + +[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Scpe_sig, Symbols, Tx_bits,"precode_mode",duob_mode); + +ml_mlse_results.metrics.print("description",'ML pre Eq. + Viterbi') \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m b/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m index 2a8ee51..7fe1e7d 100644 --- a/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m +++ b/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m @@ -8,83 +8,115 @@ db_coding_approach = 0; fsym = 160e9; fdac = 256e9; random_key = 0; -M = 4; +pams = [4]; -if (db_precode==1)&&(db_coding_approach==0) +cols = cbrewer2('Paired',6); +for i = 1:length(pams) + M = pams(i); + if (db_precode==1)&&(db_coding_approach==0) - if M == 4 - pulsef=1; - precomp_amp_max = -50; - elseif M == 6 - pulsef=0; - precomp_amp_max = -50; - elseif M == 8 - pulsef=0; - precomp_amp_max = -50; + if M == 4 + pulsef=1; + precomp_amp_max = -50; + fsym = 196e9; + elseif M == 6 + pulsef=0; + precomp_amp_max = -50; + fsym = 180e9; + elseif M == 8 + pulsef=0; + precomp_amp_max = -50; + fsym = 160e9; + end + + elseif (db_precode==1)&&(db_coding_approach==1) + + if M == 4 + pulsef=1; + precomp_amp_max = -38; + pulsef = 1; + elseif M == 6 + pulsef=0; + precomp_amp_max = -38; + pulsef = 1; + elseif M == 8 + pulsef=0; + precomp_amp_max = -38; + pulsef = 1; + end + + elseif (db_precode==0)&&(db_coding_approach==0) + + if M == 4 + pulsef=1; + precomp_amp_max = -37; + pulsef = 1; + fsym = 196e9; + elseif M == 6 + pulsef=0; + precomp_amp_max = -34; + pulsef = 1; + fsym = 180e9; + elseif M == 8 + pulsef=0; + precomp_amp_max = -34; + pulsef = 0; + fsym = 160e9; + end end -elseif (db_precode==1)&&(db_coding_approach==1) - if M == 4 - pulsef=1; - precomp_amp_max = -38; - pulsef = 1; - elseif M == 6 - pulsef=0; - precomp_amp_max = -38; - pulsef = 1; - elseif M == 8 - pulsef=0; - precomp_amp_max = -38; - pulsef = 1; + rcalpha = 0.05; + Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); + + Pamsource = PAMsource(... + "fsym",fsym,"M",M,"order",19,"useprbs",0,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.2,... + "applypulseform",pulsef,"pulseformer",Pform,... + "randkey",random_key,... + "db_precode",db_precode,"db_encode",db_coding_approach,... + "mrds_code",0,"mrds_blocklength",512); + + [Digi_sig,Symbols,Bits] = Pamsource.process(); + + Digi_sig = Digi_sig.normalize("mode","rms"); + + precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs); + + % maxampdb = [-30:-3:-50,precomp_amp_max]; + maxampdb = precomp_amp_max;%sort(maxampdb); + cols_ = cbrewer2('spectral',15); + + for j = 1:length(maxampdb) + + if maxampdb(j) == precomp_amp_max + color=clr.Set1.green; + else + color=cols_(j,:); + end + + Digi_sig_pre = precomp_est.precomp(Digi_sig,'maxampdb',maxampdb(j),'loadPath',precomp_path,'fileName',precomp_fn); + + % Digi_sig_pre = Digi_sig_pre.normalize("mode","rms"); + + Digi_sig_pre = Digi_sig_pre.resample("fs_out",fdac); + + Digi_sig_pre= Digi_sig_pre.normalize("mode","rms"); + + Digi_sig_pre.spectrum("displayname","Strong Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0,"color",color,"linestyle",'-','addDCoffset',27); + end -elseif (db_precode==0)&&(db_coding_approach==0) + Digi_sig.spectrum("displayname","No Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0,"color",cols(2*i,:),"linestyle",'-','addDCoffset',27); - if M == 4 - pulsef=1; - precomp_amp_max = -37; - pulsef = 1; - elseif M == 6 - pulsef=0; - precomp_amp_max = -34; - pulsef = 1; - elseif M == 8 - pulsef=0; - precomp_amp_max = -34; - pulsef = 0; - end end +ylim([-25,10]); +xlim([0,105]); +xticks(0:20:110); +yticks(-30:10:10); -rcalpha = 0.05; -Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); - -Pamsource = PAMsource(... - "fsym",fsym,"M",M,"order",19,"useprbs",1,... - "fs_out",fdac,... - "applyclipping",0,"clipfactor",1.2,... - "applypulseform",pulsef,"pulseformer",Pform,... - "randkey",random_key,... - "db_precode",db_precode,"db_encode",db_coding_approach,... - "mrds_code",0,"mrds_blocklength",512); - -[Digi_sig,Symbols,Bits] = Pamsource.process(); - -Digi_sig = Digi_sig.normalize("mode","rms"); - -Digi_sig.spectrum("displayname","No Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0); - -precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs); - -Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',precomp_amp_max,'loadPath',precomp_path,'fileName',precomp_fn); - -Digi_sig = Digi_sig.normalize("mode","rms"); - -Digi_sig = Digi_sig.resample("fs_out",fdac); - -Digi_sig= Digi_sig.normalize("mode","rms"); - -Digi_sig.spectrum("displayname","Strong Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0); - - +fig = gcf; +pos = [536.3333 879 450 222]; +set(fig, 'Position', pos); \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/bias_evaluation.m b/projects/HighSpeedExperiment_2024/bias_evaluation.m index 3fe8d9d..c042156 100644 --- a/projects/HighSpeedExperiment_2024/bias_evaluation.m +++ b/projects/HighSpeedExperiment_2024/bias_evaluation.m @@ -26,11 +26,14 @@ clf hold on cols = cbrewer2('Set1',3); for l = 1:numel(lambda_vals) + figure() for m = 1:numel(M_vals) ber_ffe = wh.getStoValue('ber_ffe',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l)); - ber = wh.getStoValue('ber_collect',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l)); + ber = wh.getStoValue('ber_ffe',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l)); exfo = wh.getStoValue('exfo',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l)); + + lb = wh.getStoValue('exfo',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l)); for e = 1:numel(exfo) laser_pow(e) = exfo{e}.cur_power; @@ -42,7 +45,7 @@ for l = 1:numel(lambda_vals) rx_logbook = wh.getStoValue('rx_logbook',v_bias_vals(1),awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(1),lambda_vals(1)); - subplot(1,3,l) + hold on a = scatter(v_bias_vals,min(ber,[],2),40,'LineWidth',2,'Marker','.','DisplayName',['PAM ',num2str(M_vals(m))],'MarkerEdgeColor',cols(m,:)); title([num2str(lambda_vals(l)),'nm']) @@ -66,7 +69,7 @@ for l = 1:numel(lambda_vals) % Polynomial fit (e.g., second-order polynomial) [woutliers,n] = rmoutliers( min(ber,[],2) ); - p = polyfit( v_bias_vals(~n), log10(woutliers), 4); % Adjust order as needed + p = polyfit( v_bias_vals(~n), log10(woutliers), 3); % Adjust order as needed BER_fit = polyval(p, v_bias_vals); @@ -93,6 +96,7 @@ for l = 1:numel(lambda_vals) end end +%% filename = "Z:\2024\sioe\High Speed Messungen Oktober\bias_testing_and_b2b\PAM4_b2b_bias_sweep_20241023_191202_wh_BB_BIAS_FINAL.mat"; diff --git a/projects/IMDD_base_system/imdd_it.m b/projects/IMDD_base_system/imdd_it.m index d2fdf3c..bd2461d 100644 --- a/projects/IMDD_base_system/imdd_it.m +++ b/projects/IMDD_base_system/imdd_it.m @@ -15,7 +15,7 @@ if 1 wh.addStorage("ber"); % wh = submit_simulations(wh,"parallel",0,"simulation_mode",0); - wh = submit_handle(@imdd_model,wh,"parallel",1); + wh = submit_handle(@imdd_model,wh,"parallel",0); end diff --git a/projects/IMDD_base_system/model_linewidth_evaluation.m b/projects/IMDD_base_system/model_linewidth_evaluation.m new file mode 100644 index 0000000..e648184 --- /dev/null +++ b/projects/IMDD_base_system/model_linewidth_evaluation.m @@ -0,0 +1,181 @@ +%%% Run parameters +% TX +M = 4; + +apply_pulsef = 1; +fdac = 256e9; +fadc = 256e9; +random_key = 2; + +rcalpha = 0.05; +kover = 8; +vbias_rel = 0.5; +u_pi = 3.2; +vbias = -vbias_rel*u_pi; +laser_wavelength = 1300; +laser_linewidth = logspace(0,6.2,24); + +% Channel +link_length = 10; + +alpha = 0; + +doub_mode = db_mode.no_db; +cols = linspecer(6); +rop = [-6]; +bwl = [0.5:0.1:1.5]; +fsym = [200:16:256].*1e9; +% nonlin_mod = [0.5:0.01:0.75]; +fsym = ones(size(laser_linewidth)).*fsym(1); +nonlin_mod = ones(size(laser_linewidth)).*0.5; + +ffe_results = {}; +mlse_results_lin= {}; + +parfor r = 1:length(laser_linewidth) + + Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha); + + db_precode = 0; + db_encode = 0; + duob_mode = db_mode.no_db; + apply_pulsef = 1; + + [Digi_sig,Symbols,Tx_bits] = PAMsource(... + "fsym",fsym(r),"M",M,"order",18,"useprbs",1,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.5,... + "applypulseform",apply_pulsef,"pulseformer",Pform,... + "randkey",random_key,... + "db_precode",db_precode,"db_encode",db_encode,... + "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); + + El_sig = M8199B("kover",kover).process(Digi_sig); + + %%%%% Electrical Driver Amplifier %%%%%% + El_sig = El_sig.normalize("mode","oneone"); + + %%%%% MODULATE E/O CONVERSION %%%%% + u_pi = 3.2; + vbias = -u_pi*nonlin_mod(r); + [Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth(r),"randomkey",random_key+1).process(El_sig); + + %%%%%% Fiber %%%%%% + mpi = 1; + if mpi + Combined_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); + else + + % 2) ping pong fiber propagation + mpi_path = 00; + Interference_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",mpi_path*2,"alpha",0,"D",0,"lambda0",1310,"gamma",0).process(Opt_sig); + + Interference_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",-30).process(Interference_sig); + + [Main_sig,dly] = Opt_sig.delay("delay_meter",mpi_path*2); + + % Add + Combined_sig = Main_sig + Interference_sig; + + % Cut (due to the delays there is a jump in the signals) + if dly == 0;dly = 1;end + Combined_sig.signal = Combined_sig.signal(ceil(dly):end); + + % Fiber + Combined_sig = Fiber("fsimu",Combined_sig.fs,"fiber_length",2,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.08).process(Combined_sig); + end + + + %%%%%% ROP %%%%%% + Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Combined_sig); + + %%%%%% PD Square Law %%%%%% + PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",random_key).process(Opt_sig); + + %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% + rx_bwl = 70e9; + PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig); + + % %%%%%% Low-pass Scope %%%%%% + Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + + %%%%%% Scope %%%%%% + 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',1,'H_lpf',Lp_scpe).process(PD_sig); + + Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym(r)); + % Symbols.signal = Symbols.signal(1:Scpe_sig_2sps.length/2); + + % 2sps + [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0); + Rx_sig_2sps = Scpe_cell{1}; + Rx_sig_2sps = Rx_sig_2sps.normalize("mode","rms"); + + % 1sps + Scpe_sig_1sps = Scpe_sig.resample("fs_out",1*fsym(r)); + + [~, Scpe_cell_1sps, ~, found_sync] = Scpe_sig_1sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0); + Rx_sig_1sps = Scpe_cell_1sps{1}; + Rx_sig_1sps = Rx_sig_1sps.normalize("mode","rms"); + + + %% RUN DSP + len_tr = 4096*2; + + mu_ffe1 = 0.0001; + mu_ffe2 = 0.0008; + mu_ffe3 = 0.001; + mu_dc = 0.005; + % mu_dc = 0; + + mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3]; + mu_dfe = 0.0004; + pf_ncoeffs = 1; + ffe_order = [50, 3, 3]; + mu_lms = 0.0005; + eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms"); + % eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",1,"DCmu",0.00,"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).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0); + + [ffe_results{r}, mlse_results_lin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig_2sps, Symbols, Tx_bits, ... + "precode_mode", duob_mode,... + 'showAnalysis', 0, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + + ffe_results{r}.metrics.print; + mlse_results_lin{r}.metrics.print; + +end + + +figure(1);hold on; +plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.BER, ffe_results),'DisplayName','VNLE') +plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.BER, mlse_results_lin),'DisplayName','VNLE+MLSE') +xlabel('Linewidth [GHz]'); +ylabel('BER') +set(gca,'YScale','log'); +legend; +ylim([1e-5 1e-1]); +beautifyBERplot; + +figure();hold on; +plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.AIR.*1e-9, ffe_results),'DisplayName','FFE') +plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.AIR.*1e-9, mlse_results_lin),'DisplayName','FFE+MLSE') +xlabel('Linewidth [GHz]'); +ylabel('AIR [GBd]') +% set(gca,'YScale','log'); +legend; +beautifyBERplot("logscale",0); + +figure(); hold on +stem(calcWavelengthPlan(16,400e9,1310),ones(16,1),'DisplayName','16x400','Marker','.','LineWidth',1); +stem(calcWavelengthPlan(8,800e9,1310),ones(8,1),'DisplayName','8x800','Marker','.','LineWidth',1); +stem(calcWavelengthPlan(16,800e9,1310),ones(16,1),'DisplayName','16x800','Marker','.','LineWidth',1); +ylim([0,1.2]); +ylabel('wavelength [nm]'); +xlim([1270, 1350]) \ No newline at end of file diff --git a/projects/IMDD_base_system/simulation_bwl.m b/projects/IMDD_base_system/simulation_bwl.m index 40ccbb2..e70c40d 100644 --- a/projects/IMDD_base_system/simulation_bwl.m +++ b/projects/IMDD_base_system/simulation_bwl.m @@ -1,6 +1,6 @@ %%% Run parameters % TX -M = 6; +M = 4; m = floor(log2(M)*10)/10; fsym = 224e9; @@ -47,16 +47,17 @@ doub_mode = db_mode.no_db; cols = linspecer(6); rop = [-6]; bwl = [0.5:0.1:1.5]; -fsym = [120:8:256].*1e9; -fsym =150e9; +fsym = [192:16:256].*1e9; +fsym = 208e9; -ber_vnle = []; +ber_vnle = []; ber_mlse = []; +ber_mlse_burg = []; ber_viterbi = []; ber_db = []; ber_db_diff_precoded = []; gmi_vnle_bitwise = []; -gmi_mlse = []; +gmi_mlse = []; gmi_mlse_db = []; for r = 1:length(fsym) @@ -69,16 +70,16 @@ for r = 1:length(fsym) apply_pulsef = 1; [Digi_sig,Symbols,Tx_bits] = PAMsource(... - "fsym",fsym(r),"M",M,"order",18,"useprbs",0,... + "fsym",fsym(r),"M",M,"order",19,"useprbs",0,... "fs_out",fdac,... "applyclipping",0,"clipfactor",1.5,... "applypulseform",apply_pulsef,"pulseformer",Pform,... "randkey",random_key,... "db_precode",db_precode,"db_encode",db_encode,... "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); - + % El_sig = AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig); - El_sig = M8199B("kover",kover).process(Digi_sig); + El_sig = M8199B("kover",kover).process(Digi_sig); % AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig); %%%%% Low-pass el. components %%%%%% @@ -95,15 +96,17 @@ for r = 1:length(fsym) vbias = -u_pi*0.5; [Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1).process(El_sig); - figure(15); - hold on - scatter(El_sig.signal(1:100000)+vbias,(abs(Opt_sig.signal(1:100000)).^2)*1e3,0.1,'.','DisplayName','Modulator TF') - xlabel('Input in V') - ylabel('abs(Eopt)2 in mW','Interpreter','latex') - ylim([0 2]); - xlim([-3.2 0]); - - Opt_sig.eye(fsym(r),M,"fignum",103837); + if 0 + figure(15); + hold on + scatter(El_sig.signal(1:100000)+vbias,(abs(Opt_sig.signal(1:100000)).^2)*1e3,0.1,'.','DisplayName','Modulator TF') + xlabel('Input in V') + ylabel('abs(Eopt)2 in mW','Interpreter','latex') + ylim([0 2]); + xlim([-3.2 0]); + + Opt_sig.eye(fsym(r),M,"fignum",103837); + end %%%%%% Fiber %%%%%% Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); @@ -113,50 +116,50 @@ for r = 1:length(fsym) % Opt_sig.eye(fsym(r),M,"fignum",103838); - % % Opt_sig.signal = Opt_sig.signal + 5*abs(mean(Opt_sig.signal)); + % % Opt_sig.signal = Opt_sig.signal + 5*abs(mean(Opt_sig.signal)); % Opt_sig.move_it_spectrum("displayname",'Opt Sig after Amp','fignum',1223323); % Pc = abs(mean(Opt_sig.signal)).^2; % carrier power % Ptot = mean(abs(Opt_sig.signal).^2); % total power - % Ps = max(Ptot - Pc, eps); + % Ps = max(Ptot - Pc, eps); % Pcdb = 10*log10(Pc); % Psdb = 10*log10(Ps); - % + % % cspr_dB = 10*log10(Pc / Ps); - % + % % % Minimal in-place CSPR set (real, nonnegative field constraint) % E = Opt_sig.signal; % real field samples % target_cspr_dB = 20; % <-- set your target CSPR (dB) - % + % % % Decompose into DC + zero-mean waveform % m = mean(E); % x0 = E - m; % zero-mean modulation % Ps0 = mean(x0.^2); % sideband power (fixed if shape kept) - % + % % % Current CSPR (for reference) % Pc_cur = m^2; % Ptot_cur = mean(E.^2); % Ps_cur = max(Ptot_cur - Pc_cur, eps); % cspr_in = 10*log10(Pc_cur / Ps_cur); - % + % % % Bias needed for target CSPR, and minimal bias to keep E>=0 % R_tgt = 10^(target_cspr_dB/10); % Pc/Ps % a_req = sqrt(R_tgt * Ps0); % required DC bias % a_min = -min(x0); % to avoid negatives everywhere % a = max(a_req, a_min); % if infeasible, lands at CSPR_min - % + % % % Apply bias (preserves waveform shape) % E_new = a + x0; - % + % % % Achieved CSPR % Pc_new = mean(E_new)^2; % Ptot_new = mean(E_new.^2); % Ps_new = max(Ptot_new - Pc_new, eps); % cspr_out = 10*log10(Pc_new / Ps_new); - % + % % % (Optional) show feasibility info % cspr_min = 10*log10((a_min^2)/max(Ps0,eps)); % disp(table(cspr_in, target_cspr_dB, cspr_min, cspr_out)); - % + % % % Use E_new as your adjusted field % Opt_sig.signal = E_new; @@ -166,57 +169,56 @@ for r = 1:length(fsym) %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% rx_bwl = 70e9; PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig); - + % %%%%%% Low-pass Scope %%%%%% Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); - + %%%%%% Scope %%%%%% 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',1,'H_lpf',Lp_scpe).process(PD_sig); - + Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym(r)); % Scpe_sig_resampled.signal = Scpe_sig_resampled.signal(1:2*length(Symbols)); - + [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0); Rx_sig = Scpe_cell{1}; Rx_sig = Rx_sig.normalize("mode","rms"); - if 0 + if 1 %Duobinary Targeting eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_order3],"Nb",[dfe_order],"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); - db_ref_sequence = Duobinary().encode(Symbols); db_ref_constellation = unique(db_ref_sequence.signal); [eq_signal, eq_noise] = eq_.process(Rx_sig,db_ref_sequence); - + viterbi = 0; - if viterbi + if viterbi mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); mlse_.DIR = [1,1]; - [mlse_sig_sd] = mlse_.process(eq_signal); + [eq_signal_whitened] = mlse_.process(eq_signal); else mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling); mlse_.DIR = [1,1]; - [mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); + [eq_signal_whitened,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); end - - mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(mlse_sig_sd); + + mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(eq_signal_whitened); mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M); mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M); - + tx_symbols_precoded = Duobinary().encode(Symbols); tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); - + tx_bits_precoded = PAMmapper(M,0,"eth_style",0).demap(tx_symbols_precoded); - + rx_bits_mlse = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd_precoded); [~,errors_db_diff_precoded,ber_db_diff_precoded(r),a] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); burst_db_pre(r,:) = count_error_bursts(a, 15)./numel(Tx_bits.signal); - + %B) Just determine BER rx_bits_mlse = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); [bits_mlse,errors_db,ber_db(r),a] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); @@ -225,39 +227,37 @@ for r = 1:length(fsym) fprintf('BER ber_db_diff_precoded: %.2e \n',ber_db_diff_precoded(r)); fprintf('BER Vber_dbNLE: %.2e \n',ber_db(r)); % figure();hold on;stem(1:15,burst_db(r,:),'LineWidth',1,'Color',cols(1,:));stem(1:15,burst_db_pre(r,:),'LineWidth',1,'Color',cols(2,:));set(gca, 'yscale', 'log'); - + end % FFE or VNLE eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_order3],"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.00,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); % eq = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",[50,2,2],"sps",2,"decide",0); - - [eq_signal_sd, eq_noise] = eq_.process(Rx_sig, Symbols); + + [eq_signal_fullresp, eq_noise] = eq_.process(Rx_sig, Symbols); showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ'); - [mi_gomez(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100); - [gmi_vnle_bitwise(r)] = calc_ngmi(eq_signal_sd,Symbols); - [gmi_bitwise_2(r)] = calc_gmi_bitwise(eq_signal_sd,Symbols); - snr_vnle(r) = calc_snr(Symbols, eq_signal_sd-Symbols); - - eq_signal_sd.plot("displayname",'bla','fignum',199); - eq_signal_sd.eye(fsym(r),M,"fignum",103837); - + [mi_gomez(r)] = calc_air(eq_signal_fullresp, Symbols, "skip_front", 100, "skip_end", 100); + [gmi_vnle_bitwise(r)] = calc_ngmi(eq_signal_fullresp,Symbols); + [gmi_bitwise_2(r)] = calc_gmi_bitwise(eq_signal_fullresp,Symbols); + snr_vnle(r) = calc_snr(Symbols, eq_signal_fullresp-Symbols); + + % eq_signal_fullresp.plot("displayname",'bla','fignum',199); + % eq_signal_fullresp.eye(fsym(r),M,"fignum",103837); % Hard decision on VNLE output - eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); + eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_fullresp); rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); [~,tot_err,ber_vnle(r),a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); burst_vnle(r,:) = count_error_bursts(a, 10)./tot_err; - showLevelConfusionMatrix(eq_signal_hd,Symbols,"M",M,"fignum",200,"displayname",'bla'); - showLevelScatter(eq_signal_sd,Symbols,"displayname",'VNLE Out','f_sym',fsym(r),'fignum',201); - show2Dconstellation(eq_signal_sd,Symbols,"displayname",'VNLE Out','fignum',2241); - + % showLevelConfusionMatrix(eq_signal_hd,Symbols,"M",M,"fignum",200,"displayname",'bla'); + % showLevelScatter(eq_signal_fullresp,Symbols,"displayname",'VNLE Out','f_sym',fsym(r),'fignum',201); + % show2Dconstellation(eq_signal_fullresp,Symbols,"displayname",'VNLE Out','fignum',2241); + fprintf('BER VNLE: %.2e \n',ber_vnle(r)); fprintf('NGMI VNLE: %.2f \n',gmi_vnle_bitwise(r)./m); - if 1 % Process through postfilter and MLSE @@ -268,45 +268,135 @@ for r = 1:length(fsym) pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1,"coefficients",[1,0.85]); end - mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling); - [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); - - mlse_.DIR = pf_.coefficients; + % showEQNoisePSD(eq_noise,"postfilter_taps",pf_.coefficients,"displayname",'Postfilter Burg based'); alpha(r) = pf_.coefficients(2); - [signalclass_hd,LLR,gmi_mlse(r)] = mlse_.process(mlse_sig_sd,Symbols); - mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(signalclass_hd); - rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); + alpha_vec = max(0,round(alpha(r),2)-0.2):0.025:round(alpha(r),2)+0.4; + alpha_vec = unique(sort([alpha_vec, 1, alpha(r)])); - [~,tot_err,ber_mlse(r),a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - burst_mlse(r,:) = count_error_bursts(a, 10); + gmi_mlse_ = zeros(size(alpha_vec)); + ber_mlse_ = zeros(size(alpha_vec)); + parfor a=1:numel(alpha_vec) - showLevelConfusionMatrix(mlse_sig_hd,Symbols,"M",M,"fignum",300,"displayname",'bla'); + mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling,'DIR',[1,alpha_vec(a)]); - fprintf('BER MLSE: %.2e \n',ber_mlse(r)); - fprintf('NGMI MLSE: %.5f \n',gmi_mlse(r)./m); + pf_ = Postfilter("ncoeff",1,"useBurg",0,"coefficients",[1,alpha_vec(a)]); + [eq_signal_whitened,whitened_noise] = pf_.process(eq_signal_fullresp, eq_noise); + + [signalclass_hd,LLR,gmi_mlse_(a)] = mlse_.process(eq_signal_whitened,Symbols); - levels = sort(unique(Symbols.signal(:)).'); % 1×6 - pairs = reshape(mlse_sig_hd.signal,2,[]).'; - isedge = ismember(pairs, [levels(1) levels(end)]); - isforbidden = sum(isedge,2)==2; - fprintf('Found %d forbidden transitions (even→odd edges).\n', nnz(isforbidden)); + mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(signalclass_hd); + rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); - % Process through postfilter and MLSE - pf_ncoeffs = 1; - pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); - mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); - mlse_.DIR = pf_.coefficients; - mlse_sig_sd = mlse_.process(mlse_sig_sd); - mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(mlse_sig_sd); - rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); - [~,~,ber_viterbi(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('Viterbi BER: %.2e \n',ber_viterbi(r)); + [~,tot_err,ber_mlse_(a),errpos] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + % burst_mlse(r,:) = count_error_bursts(errpos, 10); + + % if 0 + % fprintf('BER MLSE: %.2e \n',ber_mlse(r)); + % fprintf('NGMI MLSE: %.5f \n',gmi_mlse(r)./m); + % + % showLevelConfusionMatrix(mlse_sig_hd,Symbols,"M",M,"fignum",300,"displayname",'bla'); + % + % levels = sort(unique(Symbols.signal(:)).'); % 1×6 + % pairs = reshape(mlse_sig_hd.signal,2,[]).'; + % isedge = ismember(pairs, [levels(1) levels(end)]); + % isforbidden = sum(isedge,2)==2; + % fprintf('Found %d forbidden transitions (even→odd edges).\n', nnz(isforbidden)); + % + % + % % Process through postfilter and MLSE + % pf_ncoeffs = 1; + % pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + % mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + % [eq_signal_whitened,whitened_noise] = pf_.process(eq_signal_fullresp, eq_noise); + % mlse_.DIR = pf_.coefficients; + % mlse_output = mlse_.process(eq_signal_whitened); + % mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(mlse_output); + % rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); + % [~,~,ber_viterbi(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + % fprintf('Viterbi BER: %.2e \n',ber_viterbi(r)); + % end + end + + [ber_mlse(r),idx] = min(ber_mlse_); + gmi_mlse(r) = gmi_mlse_(idx); + ber_mlse_burg(r) = ber_mlse_(alpha_vec==alpha(r)); + best_alpha(r) = alpha_vec(idx); + + end + + % IR target in EQ + if 1 + + % alpha_vec = max(0,round(alpha(r),2)-0.1):0.01:min(1,round(alpha(r),2)+0.1); + plot_stuff = 0; + gmi_mlse_pr_tgt_ = zeros(size(alpha_vec)); + ber_mlse_pr_tgt_ = zeros(size(alpha_vec)); + for a = 1:numel(alpha_vec) + + alpha_vec(a) = 0.9; + eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_order3],"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.00,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); + Symbols_filt = Symbols.filter([1,alpha_vec(a)],1); + [eq_signal_prtgt, eq_noise] = eq_.process(Rx_sig, Symbols_filt); + + showLevelHistogram(eq_signal_prtgt,Symbols_filt,"displayname",'VNLE Out','fignum',201); + + if plot_stuff + % Plot the response for respective EQ targets + Symbols_filt.spectrum("displayname",'IDEAL Filtered Reference','fignum',240587); + eq_signal_whitened.spectrum("displayname",'Full tgt. EQ + PF','fignum',240587); + eq_signal_prtgt.spectrum("displayname",'Partial Resp. Target EQ','fignum',240587); + + noise_pf_out = Symbols_filt-eq_signal_whitened; + noise_pr_tgt = Symbols_filt-eq_signal_prtgt; + + noise_pf_out.spectrum("displayname",'Ideal PR - Whitening Out','fignum',240588); + noise_pr_tgt.spectrum("displayname",'Ideal PR - PR Target Out','fignum',240588); + end + + mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling,'DIR',[1,alpha_vec(a)],'debug',0); + + [signalclass_hd,LLR,gmi_mlse_pr_tgt_(a)] = mlse_.process(eq_signal_prtgt,Symbols); + + mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(signalclass_hd); + + rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); + + [~,tot_err,ber_mlse_pr_tgt_(a),errpos] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + % burst_mlse_(a,:) = count_error_bursts(errpos, 10); + + % fprintf('BER MLSE: %.2e \n',ber_mlse_pr_tgt_(a)); + % fprintf('NGMI MLSE: %.5f \n',gmi_mlse_pr_tgt_(a)./m); + + end + + [ber_mlse_pr_tgt(r),idx] = min(ber_mlse_pr_tgt_); + gmi_mlse_pr_tgt(r) = gmi_mlse_pr_tgt_(idx); + best_alpha_pr_tgt(r) = alpha_vec(idx); end + cols = cbrewer2('paired',8); + figure(); hold on + title(sprintf('%d GBd',fsym(r).*1e-9)); + scatter(alpha_vec,ber_mlse_,15,'Marker','o','LineWidth',1,'DisplayName','MLSE','MarkerEdgeColor',cols(1,:)); + scatter(best_alpha(r),ber_mlse(r),15,'Marker','o','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(2,:)); + scatter(alpha(r),ber_mlse_burg(r),25,'Marker','+','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(2,:)); + + scatter(1,ber_db_diff_precoded(r),15,'Marker','diamond','LineWidth',2,'DisplayName','Duobinary','MarkerEdgeColor',cols(4,:)); + scatter(1,ber_db(r),15,'Marker','diamond','LineWidth',2,'DisplayName','Duobinary','MarkerEdgeColor',cols(4,:)); + + scatter(alpha_vec,ber_mlse_pr_tgt_,15,'Marker','x','LineWidth',1,'DisplayName','MLSE Partial Resp tgt','MarkerEdgeColor',cols(5,:)); + scatter(best_alpha_pr_tgt(r),ber_mlse_pr_tgt(r),25,'Marker','x','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(6,:)); + + set(gca,"YScale","log"); + % ylim([1e-6 0.5]); + % xlim([0.1 1]); + drawnow; + + end @@ -413,9 +503,9 @@ m = floor(log2(M)*10)/10; figure(113+M); clf; hold on; netrates_vnle = tp.calculateNetRate(fsym.* m, ... - 'NGMI', gmi_vnle_bitwise./m, ... - 'BER', ber_vnle); -% + 'NGMI', gmi_vnle_bitwise./m, ... + 'BER', ber_vnle); +% plot(xGHz, gmi_vnle_bitwise.*xGHz, ... 'DisplayName','GMI*R VNLE', ... mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); @@ -428,15 +518,15 @@ plot(xGHz, netrates_vnle.HD.NetRate.*1e-9, ... mk.VNLE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.VNLE); -% +% % MLSE symbol-wise (if present) plot(xGHz, gmi_mlse.*xGHz, ... 'DisplayName','GMI*R MLSE', ... mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); netrates_mlse = tp.calculateNetRate(fsym.* m, ... - 'NGMI', gmi_mlse./m, ... - 'BER', ber_mlse); + 'NGMI', gmi_mlse./m, ... + 'BER', ber_mlse); plot(xGHz, netrates_mlse.SDHD.NetRate.*1e-9, ... 'DisplayName','SD+HD MLSE', ... mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE); @@ -451,8 +541,8 @@ plot(xGHz, gmi_mlse_db.*xGHz, ... mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); netrates_db = tp.calculateNetRate(fsym.* m, ... - 'NGMI', gmi_mlse_db./m, ... - 'BER', ber_db); + 'NGMI', gmi_mlse_db./m, ... + 'BER', ber_db); plot(xGHz, netrates_db.SDHD.NetRate.*1e-9, ... 'DisplayName','SD+HD DB', ... @@ -473,7 +563,7 @@ xlim([184, 256]) % Auxiliary nested helper for numerically stable log-sum-exp function s = logsumexp(a) - % LOGSUMEXP Compute log(sum(exp(a))) in a numerically stable way - m = max(a); - s = m + log(sum(exp(a - m))); +% LOGSUMEXP Compute log(sum(exp(a))) in a numerically stable way +m = max(a); +s = m + log(sum(exp(a - m))); end diff --git a/projects/IMDD_base_system/simulation_bwl_2.m b/projects/IMDD_base_system/simulation_bwl_2.m index 8dff4a2..dec8036 100644 --- a/projects/IMDD_base_system/simulation_bwl_2.m +++ b/projects/IMDD_base_system/simulation_bwl_2.m @@ -18,14 +18,14 @@ u_pi = 2.9; vbias = -vbias_rel*u_pi; laser_wavelength = 1310; laser_linewidth = 0; -tx_bw_nyquist = 0.7; +tx_bw_nyquist = 1; % Channel link_length = 1; % RX -rop = -8; -rx_bw_nyquist = 0.7; +rop = 0; +rx_bw_nyquist = 0.99; vnle_order1 = 50; vnle_order2 = 3; @@ -59,7 +59,7 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1 db_precode = 0; db_encode = 0; -duob_mode = db_mode.no_db; +duob_mode = db_mode.db_precoded; apply_pulsef = 1; [Digi_sig,Symbols,Tx_bits] = PAMsource(... "fsym",fsym,"M",M,"order",18,"useprbs",0,... @@ -67,12 +67,21 @@ apply_pulsef = 1; "applyclipping",0,"clipfactor",1.5,... "applypulseform",apply_pulsef,"pulseformer",Pform,... "randkey",random_key,... - "db_precode",db_precode,"db_encode",db_encode,... "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); +%% proof of concept +Symbols_db = Duobinary().encode(Symbols); +mim_decoded = Duobinary().decode(Symbols_db,"M",M); +rx_bits_mim_decoded = PAMmapper(M,0,"eth_style",0).demap(mim_decoded); +rx_bits_mim_decoded_.signal = circshift(rx_bits_mim_decoded.signal,0); +[~,~,ber_mim_decode,~] = calc_ber(rx_bits_mim_decoded_.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); +fprintf('BER mim: %.2e \n',ber_mim_decode); + +%% + %%%%% AWG % El_sig = M8199A("kover",kover).process(Digi_sig); @@ -127,18 +136,57 @@ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); % mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); -% FFE or VNLE -[eq_signal_sd, eq_noise] = eq_.process(Scpe_cell{1}, Symbols); +if duob_mode == db_mode.no_db -% Hard decision on VNLE output -eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); + % FFE or VNLE + [eq_signal_sd, eq_noise] = eq_.process(Scpe_cell{1}, Symbols); + + % Hard decision on VNLE output + eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); + + % Process through postfilter and MLSE + [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); + mlse_.DIR = pf_.coefficients; + mlse_sig_sd = mlse_.process(mlse_sig_sd,Symbols); + + + % BER + rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); + [~,tot_err,ber_vnle,a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_sd); + [~,tot_err,ber_mlse,a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); -% Process through postfilter and MLSE -[mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); -mlse_.DIR = pf_.coefficients; +elseif duob_mode == db_mode.db_precoded -constellation = [-3, -1, 1, 3]; -chatgpt_answer(mlse_sig_sd.signal, Symbols.signal,mlse_.DIR,constellation); + %% + [EQ_sig, Noi] = eq_.process(Scpe_cell{1},Duobinary().encode(Symbols)); -% mlse_sig_sd = mlse_.process(mlse_sig_sd,Symbols); -% mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).quantize(mlse_sig_sd); + showLevelHistogram(EQ_sig,Duobinary().encode(Symbols),"displayname",101); + + mim_decoded = Duobinary().decode(EQ_sig,"M",M); + + showLevelHistogram(mim_decoded,Symbols,"displayname",101); + + rx_bits_mim_decoded = PAMmapper(M,0,"eth_style",0).demap(mim_decoded); + + rx_bits_mim_decoded_.signal = circshift(rx_bits_mim_decoded.signal,0); + + [~,~,ber_mim_decode,~] = calc_ber(rx_bits_mim_decoded_.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + fprintf('BER mim: %.2e \n',ber_mim_decode); + + %% + mlse_sig_hd = MLSE("DIR",[1,1],"duobinary_output",1,"M",M,"trellis_states",PAMmapper(M,0).levels).process(EQ_sig,Symbols); + + mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M); + + mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M); + + rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd_decoded); + + [~,errors_db_diff_precoded,ber_db_diff_precoded,a] = calc_ber(rx_bits_mlse_decoded.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + %% + +end \ No newline at end of file diff --git a/projects/Lab_analysis/aeon_soa_measurement_lambda_plaser_pump.mat b/projects/Lab_analysis/aeon_soa_measurement_lambda_plaser_pump.mat new file mode 100644 index 0000000..eb804e8 Binary files /dev/null and b/projects/Lab_analysis/aeon_soa_measurement_lambda_plaser_pump.mat differ diff --git a/projects/Lab_analysis/aeon_soa_measurement_pump_level_sweep.mat b/projects/Lab_analysis/aeon_soa_measurement_pump_level_sweep.mat new file mode 100644 index 0000000..3db4986 Binary files /dev/null and b/projects/Lab_analysis/aeon_soa_measurement_pump_level_sweep.mat differ diff --git a/projects/Lab_analysis/amplifier_characterization.m b/projects/Lab_analysis/amplifier_characterization.m new file mode 100644 index 0000000..7af1c85 --- /dev/null +++ b/projects/Lab_analysis/amplifier_characterization.m @@ -0,0 +1,127 @@ +% Silas Amplifier characterization + +laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib'); +laser.getLaserInfo(); +laser.disableLaser; +laser.setWavelength(1310); +laser.enableLaser; + +% pm = OptPowerMeter8153A("active",[0,1],"wavelength_nm",[1550,1310]); +% pm.readPower + +% pdfa = Thor_PDFA(); +%% +params.laserpower = [-30,-20,-10,0]; +params.lambda = [1260:5:1360]; %calcWavelengthPlan(16, 400e9 , 1310); +params.pump = [50,75,100]; + +wh = DataStorage(params); +wh.addStorage("spectrum_osa"); +wh.addStorage("wavelength_osa"); +wh.addStorage("psig_osa"); +wh.addStorage("pase_osa"); +wh.addStorage("osnr_osa"); +wh.addStorage("amp_gain"); +wh.addStorage("psig_total"); + +cols = linspecer(numel(params.laserpower)); +ccnt = 0; +for p = params.laserpower + ccnt=ccnt+1; + laser.setWavelength(params.lambda(1)); + usrcmd = sprintf('Laser to %d dBm',p); + waitUntilClick('Text',usrcmd); + + for pmp = params.pump + % pdfa.setPumpLevel(pmp); + usrcmd = sprintf('Amp to %d mA',2*pmp); + waitUntilClick('Text',usrcmd); + + for w = params.lambda + laser.setWavelength(w) + % pm.setWavelength([1550, w]); + span_nm = 5; + lambda_c_nm = w; + osa = OSA_Advantest( ... + 'span_nm', 40, ... + 'lambda_c_nm', w, ... + 'resolution_nm', 0.1, ... + 'sampling_points', 1001 ); + osa.configure(); + + + + % OSA + [lambda_nm, psd_dBm] = osa.measure(); + osnr = osa.computeOSNR('bw_signal_nm',0.01,'ref_bw_nm',0.1,'debugplots',0,... + 'carrier_frequency',w,'noise_guard_nm',1,'noise_window_nm',1); + + osa.plot("FigureNumber",pmp+1,"Color",cols(ccnt,:)); + xlabel('Wavelength [nm]'); + ylabel('Opt. Spectrum [dBm]'); + xlim([params.lambda(1)-5 params.lambda(end)+5]) + grid minor + + % Powermeter + totalpower=10*log10(sum(10.^(psd_dBm./10))); + % [totalpower]=pm.readPower; + % totalpower=totalpower(2) + 20; %20dB attenuation with eigenlight to satisfy 3dB max of PMeter + + wh.addValueToStorage(lambda_nm,'wavelength_osa',p,w,pmp); + wh.addValueToStorage(psd_dBm,'spectrum_osa',p,w,pmp); + wh.addValueToStorage(osnr.p_ase_db,'pase_osa',p,w,pmp); + wh.addValueToStorage(osnr.p_sig_db,'psig_osa',p,w,pmp); + wh.addValueToStorage(osnr,'osnr_osa',p,w,pmp); + wh.addValueToStorage(totalpower,'psig_total',p,w,pmp); + + end + end + + % before tuning all the way back to 1270nm set PDFA off + % pdfa.setPumpLevel(0); + % pdfa.disablePDFA; +end + + + + +%% OSNR + +figure(2);hold on +for p = [-30:10:0] + pmp = 100; + osnr = wh.getStoValue('osnr_osa',p,params.lambda,pmp); + osnr_measured_dB = cellfun(@(x) x.measured_dB, osnr); + osnr_direct_dB = cellfun(@(x) x.direct_dB, osnr); + plot(params.lambda,osnr_direct_dB,'DisplayName',sprintf('P_{in}: %d dB ',p)); +end +legend + +%% P out + +figure(3);hold on +for p = [-30:10:0] + pmp = 100; + psig_osa = wh.getStoValue('psig_osa',p,params.lambda,pmp)+3; + pl=plot(params.lambda,psig_osa,'DisplayName',sprintf('P_{signal}: %d dB ',p)); + + psig_powermeter = wh.getStoValue('psig_total',p,params.lambda,pmp)+3; + plot(params.lambda,psig_powermeter,'DisplayName',sprintf('P_{total}: %d dB ',p),'LineStyle','--','Color',pl.Color); + + yline(p,'Color',pl.Color,'DisplayName',sprintf('P_{total}: %d dB ',p)); +end +legend + +%% GAIN + +figure(3);hold on +for p = [-30:10:0] + + pmp = 50; + psig_osa = wh.getStoValue('psig_osa',p,params.lambda,pmp); + % psig_powermeter = wh.getStoValue('psig_powermeter',p,params.lambda,pmp); + + g = psig_osa-p; + pl=plot(params.lambda,g,'DisplayName',sprintf('P_{in}: %d dB ',p)); +end +legend \ No newline at end of file diff --git a/projects/Lab_analysis/amplifier_input_output_curve.m b/projects/Lab_analysis/amplifier_input_output_curve.m new file mode 100644 index 0000000..c95b46f --- /dev/null +++ b/projects/Lab_analysis/amplifier_input_output_curve.m @@ -0,0 +1,111 @@ +% Silas Amplifier characterization + +laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib'); +laser.getLaserInfo(); +laser.disableLaser; +laser.setWavelength(1310); +laser.enableLaser; + +pdfa = Thor_PDFA(); +%% +params.laserpower = [-30,-20,-10,0]; +params.lambda = [1310]; %calcWavelengthPlan(16, 400e9 , 1310); +params.pump = [0:5:100]; + +wh = DataStorage(params); +wh.addStorage("spectrum_osa"); +wh.addStorage("wavelength_osa"); +wh.addStorage("psig_osa"); +wh.addStorage("pase_osa"); +wh.addStorage("osnr_osa"); +wh.addStorage("amp_gain"); +wh.addStorage("psig_total"); + +cols = linspecer(numel(params.laserpower)); +ccnt = 0; +for p = params.laserpower + ccnt=ccnt+1; + laser.setWavelength(params.lambda(1)); + usrcmd = sprintf('Laser to %d dBm',p); + waitUntilClick('Text',usrcmd); + + + for w = params.lambda + laser.setWavelength(w) + % pm.setWavelength([1550, w]); + span_nm = 5; + lambda_c_nm = w; + osa = OSA_Advantest( ... + 'span_nm', 40, ... + 'lambda_c_nm', w, ... + 'resolution_nm', 0.1, ... + 'sampling_points', 1001 ); + osa.configure(); + + + for pmp = params.pump + % pdfa.setPumpLevel(pmp); + usrcmd = sprintf('Amp to %d %',pmp); + waitUntilClick('Text',usrcmd); + + % OSA + [lambda_nm, psd_dBm] = osa.measure(); + osnr = osa.computeOSNR('bw_signal_nm',0.01,'ref_bw_nm',0.1,'debugplots',0,... + 'carrier_frequency',w,'noise_guard_nm',1,'noise_window_nm',1); + + osa.plot("FigureNumber",abs(p)+10,"Color",cols(ccnt,:)); + xlabel('Wavelength [nm]'); + ylabel('Opt. Spectrum [dBm]'); + grid minor + + % Powermeter + totalpower=10*log10(sum(10.^(psd_dBm./10))); + % [totalpower]=pm.readPower; + % totalpower=totalpower(2) + 20; %20dB attenuation with eigenlight to satisfy 3dB max of PMeter + + wh.addValueToStorage(lambda_nm,'wavelength_osa',p,w,pmp); + wh.addValueToStorage(psd_dBm,'spectrum_osa',p,w,pmp); + wh.addValueToStorage(osnr.p_ase_db,'pase_osa',p,w,pmp); + wh.addValueToStorage(osnr.p_sig_db,'psig_osa',p,w,pmp); + wh.addValueToStorage(osnr,'osnr_osa',p,w,pmp); + wh.addValueToStorage(totalpower,'psig_total',p,w,pmp); + + end + end +end + + +%% P out + +figure(3);hold on +for p = [-30:10:0] + + psig_osa = wh.getStoValue('psig_osa',p,params.lambda,params.pump); + pl=plot(params.pump,psig_osa,'DisplayName',sprintf('P_{signal}: %d dB ',p)); + + psig_total = wh.getStoValue('psig_total',p,params.lambda,params.pump); + plot(params.pump,psig_total,'DisplayName',sprintf('P_{total}: %d dB ',p),'LineStyle','--','Color',pl.Color); + + pase = wh.getStoValue('pase_osa',p,params.lambda,params.pump); + plot(params.pump,pase,'DisplayName',sprintf('P_{ASE}: %d dB ',p),'LineStyle',':','Color',pl.Color); + + +end +legend + + + +%% Gain + +figure(4);hold on +for p = [-30:10:0] + + % Die -2dB sind Korrekturfaktor aus dem Setup! Gilt für PDFA und SOA + psig_osa = wh.getStoValue('psig_osa',p,params.lambda,params.pump); + pl=plot(params.pump,psig_osa-(p-2),'DisplayName',sprintf('Gain: %d dB ',p)); + + psig_total = wh.getStoValue('psig_total',p,params.lambda,params.pump); + pl=plot(params.pump,psig_total-(p-2),'DisplayName',sprintf('Gain: %d dB ',p),'Color',pl.Color,'LineStyle','--'); + +end +legend \ No newline at end of file diff --git a/projects/Lab_analysis/available_output_power_exfo_t100_oband.fig b/projects/Lab_analysis/available_output_power_exfo_t100_oband.fig new file mode 100644 index 0000000..f7af6a4 Binary files /dev/null and b/projects/Lab_analysis/available_output_power_exfo_t100_oband.fig differ diff --git a/projects/Lab_analysis/available_output_power_exfo_t100_oband_20_averages.fig b/projects/Lab_analysis/available_output_power_exfo_t100_oband_20_averages.fig new file mode 100644 index 0000000..5c672cb Binary files /dev/null and b/projects/Lab_analysis/available_output_power_exfo_t100_oband_20_averages.fig differ diff --git a/projects/Lab_analysis/laser_characterization.m b/projects/Lab_analysis/laser_characterization.m new file mode 100644 index 0000000..bf7d8f6 --- /dev/null +++ b/projects/Lab_analysis/laser_characterization.m @@ -0,0 +1,54 @@ +laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib'); +laser.getLaserInfo(); + +pm = OptPowerMeter8153A("active",[0,1],"wavelength_nm",[1550,start_wavelength]); +pm.readPower + +laserparams.lambda = [1260:2:1360]; +laserparams.laserpower = [0,3,6,8,10]; +laser_output_wh = DataStorage(laserparams); +laser_output_wh.addStorage("plaser"); + +pm = OptPowerMeter8153A("active",[0,1],"wavelength_nm",[1550,1310]); +laser.setWavelength(1310); + +cw_pwr = NaN(numel(laserparams.laserpower),numel(laserparams.lambda)); +row = 1; +for p = laserparams.laserpower + laser.setPower(p); + cnt = 1; + for w = laserparams.lambda + laser.setWavelength(w); + pm.setWavelength([1550,w]); + + for i = 1:20 + [totalpower]=pm.readPower; + cw_measurement(i)=totalpower(2)+10; + cw_pwr(row,cnt) = mean(cw_measurement); + pause(3); + end + + laser_output_wh.addValueToStorage(cw_measurement,'plaser',w,p); + + figure(2027);clf + plot(laserparams.lambda,cw_pwr(1:row,:),'Marker','*'); + xlabel('Wavelength [nm]'); + ylabel('Laser Output Power [dBm]'); + ylim([0, 11]); + xlim([laserparams.lambda(1) laserparams.lambda(end)]) + grid minor + + cnt = cnt+1; + end + row = row+1; +end + +figure();hold on +for p = laserparams.laserpower + plot(laserparams.lambda,laser_output_wh.getStoValue('plaser',laserparams.lambda,p)) +end +xlabel('Wavelength [nm]'); +ylabel('Laser Output Power [dBm]'); +ylim([0, 11]); +xlim([laserparams.lambda(1) laserparams.lambda(end)]) +grid minor \ No newline at end of file diff --git a/projects/Lab_analysis/laser_outputpower_sweep_1.mat b/projects/Lab_analysis/laser_outputpower_sweep_1.mat new file mode 100644 index 0000000..107034a Binary files /dev/null and b/projects/Lab_analysis/laser_outputpower_sweep_1.mat differ diff --git a/projects/Lab_analysis/laser_outputpower_sweep_2_many_averages.mat b/projects/Lab_analysis/laser_outputpower_sweep_2_many_averages.mat new file mode 100644 index 0000000..828136b Binary files /dev/null and b/projects/Lab_analysis/laser_outputpower_sweep_2_many_averages.mat differ diff --git a/projects/Lab_analysis/pdfa_thorlabs_pin_vs_pout.fig b/projects/Lab_analysis/pdfa_thorlabs_pin_vs_pout.fig new file mode 100644 index 0000000..f3c74b0 Binary files /dev/null and b/projects/Lab_analysis/pdfa_thorlabs_pin_vs_pout.fig differ diff --git a/projects/Lab_analysis/pdfa_thorlabs_pout.fig b/projects/Lab_analysis/pdfa_thorlabs_pout.fig new file mode 100644 index 0000000..4c5e6d2 Binary files /dev/null and b/projects/Lab_analysis/pdfa_thorlabs_pout.fig differ diff --git a/projects/Lab_analysis/thorlabs_pdfa_100prozent_sweep.mat b/projects/Lab_analysis/thorlabs_pdfa_100prozent_sweep.mat new file mode 100644 index 0000000..c02f5e3 Binary files /dev/null and b/projects/Lab_analysis/thorlabs_pdfa_100prozent_sweep.mat differ diff --git a/projects/Lab_analysis/thorlabs_pdfa_measurement_lambda_plaser_pump.mat b/projects/Lab_analysis/thorlabs_pdfa_measurement_lambda_plaser_pump.mat new file mode 100644 index 0000000..e2be0b1 Binary files /dev/null and b/projects/Lab_analysis/thorlabs_pdfa_measurement_lambda_plaser_pump.mat differ diff --git a/projects/Lab_analysis/thorlabs_pdfa_measurement_pump_level_sweep.mat b/projects/Lab_analysis/thorlabs_pdfa_measurement_pump_level_sweep.mat new file mode 100644 index 0000000..44a7aa2 Binary files /dev/null and b/projects/Lab_analysis/thorlabs_pdfa_measurement_pump_level_sweep.mat differ diff --git a/projects/Lab_base_system/exfo_control_app.mlapp b/projects/Lab_base_system/exfo_control_app.mlapp new file mode 100644 index 0000000..cbd306d Binary files /dev/null and b/projects/Lab_base_system/exfo_control_app.mlapp differ diff --git a/projects/Lab_base_system/exfo_laser_example.m b/projects/Lab_base_system/exfo_laser_example.m new file mode 100644 index 0000000..a2c28c7 --- /dev/null +++ b/projects/Lab_base_system/exfo_laser_example.m @@ -0,0 +1,15 @@ +%% Laser +start_wavelength = 1310; +laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib'); +laser.getLaserInfo(); + +laser.setWavelength(start_wavelength) +laser.setPower(0); + +laser.enableLaser(); +% for l = 1550:1560 +% laser.setWavelength(l); +% end + +laser.disableLaser(); + diff --git a/projects/Lab_base_system/olaf_control.m b/projects/Lab_base_system/olaf_control.m new file mode 100644 index 0000000..d9c72de --- /dev/null +++ b/projects/Lab_base_system/olaf_control.m @@ -0,0 +1,74 @@ + +clear all + +visastring = "GPIB0::22::INSTR"; +v_pwrmtr = visadev(visastring); +writeline(v_pwrmtr, "*IDN?"); +answ = readline(v_pwrmtr); +disp(['Yay! We can talk to Instrument: ',char(answ)]); + +% laser = Exfo_laser("mainframe_channel",3,"safety_mode",0,"connection_id",'15','lab_interface','gpib'); +% laser.getLaserInfo(); + +L = OptLaserN7714A("GPIB0::20::INSTR"); + +% visastring = 'GPIB0::10::INSTR'; +% v_lsr = visadev(visastring); +% writeline(v_lsr, "*IDN?"); +% answ = readline(v_lsr); +% disp(['Yay! We can talk to Instrument: ',char(answ)]); + +steps = 100; +wavelength_sweep = linspace(1554,1556,steps); +start_wavelength = wavelength_sweep(1); + +% laser.setWavelength(start_wavelength); +L.setWavelength(start_wavelength,0); +% laser.setPower(0); +% laser.enableLaser(); +L.setPower(0, true,0); + + +pwr_save = NaN(size(wavelength_sweep)); +for l = 1:numel(wavelength_sweep) + + % 1) + % laser.setWavelength(wavelength_sweep(l)); + L.setWavelength(wavelength_sweep(l),0); + %writeline(v_lsr, ['L=' num2str(wavelength_sweep(l))]); + + % 2) Set Wavelength of Power Meter + t = string(['SENS2:POW:WAVE ',num2str(wavelength_sweep(l)),'NM']); + writeline(v_pwrmtr, t); + % writeline(v, "SENS:POW:WAVE?"); + % wavel = readline(v); + + % 3) Read Power of + writeline(v_pwrmtr, "READ2:POW?"); + pwr = readline(v_pwrmtr); + pwr_save(l) = str2num(pwr); + + figure(2025);clf + plot(wavelength_sweep,pwr_save,'Marker','*'); + xlabel('Wavelength [nm]'); + ylabel('Insertion Loss [dB]'); + ylim([min(pwr_save)-1, min(pwr_save)+1]); + xlim([wavelength_sweep(1) wavelength_sweep(end)]) + grid minor + +end + +% laser.disableLaser(); +L.setPower(0, false,0); +L.delete(); + +%writeline(v_lsr, 'DISABLE'); + +path= "Z:\2025\NFT WDM DFG Olaf\lambda_vs_power"; + +dt = datetime('now','Format','yyyyMMdd_HHmmss'); +filename = fullfile(path, "loop_5_7_"+num2str(wavelength_sweep(1)) +"nm_to_"+ num2str(wavelength_sweep(end))+ "nm_" + string(dt) + ".mat"); + +save(filename, "pwr_save","wavelength_sweep"); + +disp("Saved to: " + filename); diff --git a/projects/Lab_base_system/olaf_laser_att_control_lambda_sweep.m b/projects/Lab_base_system/olaf_laser_att_control_lambda_sweep.m new file mode 100644 index 0000000..91fe52e --- /dev/null +++ b/projects/Lab_base_system/olaf_laser_att_control_lambda_sweep.m @@ -0,0 +1,61 @@ + +clear all + +%%%%%%%%%%%%%%%%%%%%% + +start_wavelength = 1554.2; %nm +stop_wavelength = 1555.7; %nm +steps = 100; +power = 0; %dBm +name = "south_board_total_loss_ports_21_20"; + +%%%%%%%%%%%%%%%%%%%%% + +wavelength_sweep = linspace(start_wavelength,stop_wavelength,steps); +wavelength_1 = wavelength_sweep(1); + +visastring = "GPIB0::22::INSTR"; +v_pwrmtr = visadev(visastring); +%writeline(v_pwrmtr, "*IDN?"); +%answ = readline(v_pwrmtr); +%disp(['Yay! We can talk to Instrument: ',char(answ)]); + +L = OptLaserN7714A("GPIB0::20::INSTR"); +L.setWavelength(wavelength_1,0); +L.setPower(power, true,0); + +pwr_save = NaN(size(wavelength_sweep)); +for l = 1:numel(wavelength_sweep) + + % 1) + L.setWavelength(wavelength_sweep(l),0); + + % 2) Set Wavelength of Power Meter + t = string(['SENS1:POW:WAVE ',num2str(wavelength_sweep(l)),'NM']); + writeline(v_pwrmtr, t); + + % 3) Read Power of + writeline(v_pwrmtr, "READ1:POW?"); + pwr = readline(v_pwrmtr); + pwr_save(l) = str2num(pwr); + + figure(2026);clf + plot(wavelength_sweep,pwr_save,'Marker','*'); + xlabel('Wavelength [nm]'); + ylabel('Insertion Loss [dB]'); + ylim([min(pwr_save)-1, max(pwr_save)+1]); + xlim([wavelength_sweep(1) wavelength_sweep(eind)]) + grid minor +end + +L.setPower(0, false,0); +L.delete(); + +path= "Z:\2025\NFT WDM DFG Olaf\lambda_vs_power"; + +dt = datetime('now','Format','yyyyMMdd_HHmmss'); +filename = fullfile(path, name+"_"+replace(num2str(wavelength_sweep(1)),".","_") +"nm_to_"+ replace(num2str(wavelength_sweep(end)),".","_")+ "nm_" + string(dt) + ".mat"); + +save(filename, "pwr_save","wavelength_sweep"); + +disp("Saved to: " + filename); diff --git a/projects/Lab_base_system/olaf_laser_control.m b/projects/Lab_base_system/olaf_laser_control.m new file mode 100644 index 0000000..cdde961 --- /dev/null +++ b/projects/Lab_base_system/olaf_laser_control.m @@ -0,0 +1,21 @@ +clear all + +% L = OptLaserN7714A("TCPIP0::134.245.243.209::inst0::INSTR"); +% L.setWavelength(1551,1); % nm +% L.setPower(5.8, true,1); % dBm, also turns output ON +% L.setDither(0,1); +% % L.setPower(5.9, false,1); +% info = L.getLaserInfo(); +% +% L.delete(); + + +clear all + +L = OptLaserN7714A("GPIB0::20::INSTR"); +L.setWavelength(1550,0); % nm +L.setPower(0, true,0); % dBm, also turns output ON +L.setPower(0, false,0); +info = L.getLaserInfo(); + +L.delete(); \ No newline at end of file diff --git a/projects/Lab_base_system/olaf_laser_control_minimal.m b/projects/Lab_base_system/olaf_laser_control_minimal.m new file mode 100644 index 0000000..cbe68d1 --- /dev/null +++ b/projects/Lab_base_system/olaf_laser_control_minimal.m @@ -0,0 +1,29 @@ +% --- Minimal N7714A example (MATLAB) --- +visa_addr = "TCPIP0::134.245.243.209::inst0::INSTR"; % <-- change to your laser's IP +port = 1; % 1..4 selects the N7714A output port + +% v = visadev(visa_addr); + +% Identify instrument +writeline(v,'*IDN?'); disp(strtrim(readline(v))); + +% Make sure wavelength is settable (Auto mode) +writeline(v, sprintf('SOUR%d:WAV:AUTO 1', port)); % Auto Mode ON (wavelength set allowed). + +% Set wavelength and power (units explicit) +writeline(v, sprintf('SOUR%d:WAV 1560NM', port)); % set 1550 nm. :contentReference[oaicite:3]{index=3} +writeline(v, sprintf('SOUR%d:POW:UNIT DBM', port)); % power unit = dBm. :contentReference[oaicite:4]{index=4} +writeline(v, sprintf('SOUR%d:POW:LEV:IMM:AMPL 6 DBM', port)); % set -3 dBm. :contentReference[oaicite:5]{index=5} + +% Enable output (either OUTP or SOUR:POW:STAT works) +writeline(v, sprintf('OUTP%d:STAT ON', port)); % laser ON. :contentReference[oaicite:6]{index=6} +writeline(v, 'OUTP1 ON'); + +% ---- Read back to verify ---- +writeline(v, sprintf('SOUR%d:WAV?', port)); wav_m = str2double(readline(v)); +writeline(v, sprintf('SOUR%d:POW:LEV:IMM:AMPL?', port)); pow = str2double(readline(v)); +writeline(v, sprintf('OUTP%d:ON?', port)); onoff = str2double(readline(v)); + +fprintf('Port %d -> %.2f nm, %.2f dBm, Output=%d\n', port, 1e9*wav_m, pow, onoff); + +delete(v); \ No newline at end of file diff --git a/projects/Lab_base_system/olaf_laser_control_single_wavelength_OFF.m b/projects/Lab_base_system/olaf_laser_control_single_wavelength_OFF.m new file mode 100644 index 0000000..1119c82 --- /dev/null +++ b/projects/Lab_base_system/olaf_laser_control_single_wavelength_OFF.m @@ -0,0 +1,2 @@ +L.setPower(power, false,0); +L.delete(); diff --git a/projects/Lab_base_system/olaf_laser_control_single_wavelength_ON.m b/projects/Lab_base_system/olaf_laser_control_single_wavelength_ON.m new file mode 100644 index 0000000..3b00905 --- /dev/null +++ b/projects/Lab_base_system/olaf_laser_control_single_wavelength_ON.m @@ -0,0 +1,19 @@ +clear all + +% 1554.7097 +% 1554.9032 +% 1555.0968 +% 1555.2904 + + +%%%%%%%%%%%%%%%%%%%%% + +wavelength = 1555.0968; %nm +power = 6; %dBm + +%%%%%%%%%%%%%%%%%%%%% + +L = OptLaserN7714A("GPIB0::20::INSTR"); + +L.setWavelength(wavelength,0); +L.setPower(power, true,0); diff --git a/projects/ML_based_MLSE/analyze_filter_length.m b/projects/ML_based_MLSE/analyze_filter_length.m new file mode 100644 index 0000000..616b715 --- /dev/null +++ b/projects/ML_based_MLSE/analyze_filter_length.m @@ -0,0 +1,164 @@ +%% analyze_filter_length.m +clear; clc; + +M = 4; +randkey = 1; + +% --- Parameter sweep +order_range = 2:3:11; % FFE order +delta_range = 0:2:4; % delta +SNR_dB = 20; + +% --- Prepare bit sequence +order_bits = 19; +s = RandStream('twister','Seed',randkey); +for i = 1:log2(M) + N = 2^(order_bits-1); + bitpattern(:,i) = randi(s,[0 1], N, 1); +end +Bits = Informationsignal(bitpattern); +Symbols = PAMmapper(M,0).map(Bits); +Symbols.fs = 200e9; + +% --- Channel (minimal ISI + AWGN) +h = [0.3 0.9 0.3]; h = h/norm(h); +symbols_filt = Symbols.filter(h,1); +symbols_noi = symbols_filt; +symbols_noi.signal = awgn(symbols_filt.signal,SNR_dB,'measured'); + +% --- Generate all parameter pairs +[O,D] = ndgrid(order_range, delta_range); +pairs = [O(:), D(:)]; + +training_len = 100; + +ber_vec = nan(size(pairs,1),1); % initialize with NaN +ber_training = nan(size(pairs,1),training_len); +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) + order_k = pairs(k,1); + delta_k = pairs(k,2); + + % Skip invalid combinations (delay cannot exceed filter length) + if abs(delta_k) >= order_k + fprintf('Skip: order=%d, delta=%d (invalid)\n', order_k, delta_k); + continue; + end + + try + ml = ML_MLSE("epochs_tr",training_len,"epochs_dd",1,"len_tr",2^15, ... + "mu_dd",0.1,"mu_tr",0.1,"order",order_k,"sps",1, ... + "traceback_depth",128,"L",3,"delta",delta_k,"adaptive_mu",0); + + [y_ml,y_ref] = ml.process(symbols_noi,Symbols); + ref_bits = PAMmapper(M,0).demap(y_ref); + eq_bits = PAMmapper(M,0).demap(y_ml); + + ber_training(k,:) = ml.ber; + ce_training(k,:) = ml.ce; + + [~,~,ber_vec(k)] = calc_ber(eq_bits.signal, ref_bits.signal, ... + "skip_front",10,"skip_end",10); + L = min(length(ml.ce),30); + ce_vec(k) = mean(ml.ce(end-L+1:end)); + + fprintf('order=%d, delta=%d → BER=%.2e, CE=%.3f\n', ... + order_k, delta_k, ber_vec(k), ce_vec(k)); + catch ME + fprintf('Error at order=%d, delta=%d: %s\n', ... + order_k, delta_k, ME.message); + ber_vec(k) = NaN; + ce_vec(k) = NaN; + end +end + +% --- reshape to 2D matrices +ber_mat = reshape(ber_vec, numel(order_range), numel(delta_range)); +ce_mat = reshape(ce_vec, numel(order_range), numel(delta_range)); + + +%% --- Plot BER +figure; hold on +cols = cbrewer2('Set1',10); +for i = 1:numel(delta_range) + plot(order_range,ber_mat(:,i),'DisplayName',sprintf('delta: %d',delta_range(i)),'Color',cols(i,:)) +end +beautifyBERplot +ylabel('BER'); xlabel('Filter Order [N]'); +title('BER vs. Filter order'); +ylim([1e-4, 0.1]); +yline(3.8e-3,'HandleVisibility','off'); +yline(2.2e-4,'HandleVisibility','off'); + +%% --- Plot Cross-Entropy +figure; hold on +for i = 1:numel(delta_range) + plot(order_range,ce_mat(:,i),'DisplayName',sprintf('delta: %d',delta_range(i))) +end +% beautifyBERplot +ylabel('BER'); xlabel('Filter Order [N]'); +title('BER vs. Filter order'); + +%% --- Training Curves: BER and CE per combination +figure('Name','Training Convergence'); hold on +cols = cbrewer2('Set1', 10); % one color per delta + +[O, D] = ndgrid(order_range, delta_range); + +for i = 1:size(ber_training,1) + ord = O(i); + del = D(i); + + if ord <= del + continue; + end + % --- show only order 2 and 10 + if ord == 2 + lnst = '-'; + elseif ord == 5 + lnst = ':'; + elseif ord == 8 + lnst = '--'; + elseif ord == 11 + lnst = '-.'; + end + + + b = ber_training(i,:); + + + plot_label = sprintf('order=%d, delta=%d', ord, del); + plot(1:length(b), b, 'Color', cols(del+1, :), ... + 'DisplayName', plot_label,'LineStyle',lnst); +end + +set(gca,'YScale','log'); +xlabel('Epoch'); +ylabel('BER'); +title('Training Convergence (BER)'); +legend('show'); +grid on; + + +%% --- Cross-Entropy curves +figure('Name','Cross-Entropy'); hold on +cols = cbrewer2('Set1',size(ce_training,1)); +for i = 1:size(ce_training,1) + + [O, D] = ndgrid(order_range, delta_range); + plot_label = sprintf('order=%d, delta=%d', O(i), D(i)); + c = ce_training(i,:); + c(~isfinite(c) | c==0) = NaN; + if all(isnan(c)), continue; end + plot(1:length(c), c, 'Color', cols(D(i)+1,:), ... + 'DisplayName', plot_label); +end +set(gca,'YScale','log'); +xlabel('Epoch'); +ylabel('Cross-Entropy'); +title('Training Convergence (CE)'); +legend('show'); +grid on; diff --git a/projects/ML_based_MLSE/analyze_mu.m b/projects/ML_based_MLSE/analyze_mu.m new file mode 100644 index 0000000..044b71a --- /dev/null +++ b/projects/ML_based_MLSE/analyze_mu.m @@ -0,0 +1,113 @@ + +M = 4; +order = 19; +randkey = 1; + +bitpattern = []; +s = RandStream('twister','Seed',randkey); +for i = 1:log2(M) + N = 2^(order-1); %length of prbs + bitpattern(:,i) = randi(s,[0 1], N, 1); +end + +if M == 6 + bitpattern = reshape(bitpattern',[],1); + bitpattern = bitpattern(1:end-mod(length(bitpattern),5)); +end + +Bits = Informationsignal(bitpattern); + +Symbols = PAMmapper(M,0).map(Bits); +Symbols.fs = 200e9; + +Bits_ = PAMmapper(M, 0, "eth_style", 0).demap(Symbols); + +% --- Channel: minimal ISI response + AWGN --- +h = [0.3 0.9 0.3]; % impulse response (normalized later if desired) +h = h / norm(h); % optional normalization for unit energy + +symbols_filt = Symbols.filter(h,1); + + +%% SHOW Loss during training + +mu = logspace(-3,-0.8,12); +ber_ml_mlse = zeros(size(mu)); +ber_training = []; +ce_training = []; + +parfor i = 1:numel(mu) + + symbols_noi = symbols_filt; + SNR_dB = 20; + symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB, 'measured'); % AWGN with given SNR + + ml_mlse_equalizer = ML_MLSE("epochs_tr",200,"epochs_dd",1,"len_tr",2^15,... + "mu_dd",mu(i),"mu_tr",mu(i),"order",5,"sps",1,... + "traceback_depth",128,"L",3,"delta",0,'adaptive_mu',0); + + [y_ml_mlse,y_ref] = ml_mlse_equalizer.process(symbols_noi,Symbols); + ref_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ref); + ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + [~, errors, ber_ml_mlse(i), errpos] = calc_ber(ml_mlse_bits.signal, ref_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse(i)); + ber_training(i,:) = ml_mlse_equalizer.ber; + ce_training(i,:) = ml_mlse_equalizer.ce; +end + +%% +symbols_noi = symbols_filt; +SNR_dB = 20; +symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB, 'measured'); % AWGN with given SNR +ml_mlse_equalizer_adap = ML_MLSE("epochs_tr",200,"epochs_dd",1,"len_tr",2^16,... + "mu_dd",1,"mu_tr",1,"order",5,"sps",1,... + "traceback_depth",128,"L",3,"delta",0,"adaptive_mu",1); + +[y_ml_mlse,y_ref] = ml_mlse_equalizer_adap.process(symbols_noi,Symbols); +ref_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ref); +ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); +[~, errors, ber_ml_mlse_, errpos] = calc_ber(ml_mlse_bits.signal, ref_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); +fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_); + +%% +figure();hold on + +plot(mu,ber_ml_mlse,'DisplayName','ML-based MLSE'); + +beautifyBERplot; +xlim([mu(1), mu(end)]); +xlabel('mu'); +ylabel('BER'); +title('PAM-4; M=3; AWGN Channel'); +ylim([1e-5 0.1]); + +%% +figure() +hold on; +cols = cbrewer2('Spectral',12); +for i = 1:12 + plot(1:200,ber_training(i,:),'DisplayName', sprintf('mu=%.3f ',mu(i)),'Color',cols(i,:)); +end +set(gca,'YScale','log'); +xlabel('Epoch'); +ylabel('BER'); +title('PAM-4; L=3; SNR=20; AWGN Channel'); +plot(1:200,ml_mlse_equalizer_adap.ber,'DisplayName','Adaptive mu'); + +%% +figure() +hold on; +cols = cbrewer2('Spectral',12); +for i = 1:12 + plot(1:200,ce_training(i,:),'DisplayName', sprintf('mu=%.3f ',mu(i)),'Color',cols(i,:)); +end +set(gca,'YScale','log'); +xlabel('Epoch'); +ylabel('Cross-Entropy'); +title('PAM-4; L=3; SNR=20; AWGN Channel'); +plot(1:200,ml_mlse_equalizer_adap.ce,'DisplayName','Adaptive mu'); + + +%% SUPER LONG EPOCHS + + diff --git a/projects/ML_based_MLSE/experimental_data.m b/projects/ML_based_MLSE/experimental_data.m new file mode 100644 index 0000000..a594a92 --- /dev/null +++ b/projects/ML_based_MLSE/experimental_data.m @@ -0,0 +1,163 @@ + + +dsp_options.storage_path = 'Z:\2024\sioe_labor\'; +dsp_options.max_occurences = 1; +database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' ); +run_id = 2776; +dataTable = queryRunid(run_id, database); +fsym = dataTable.symbolrate; +M = double(dataTable.pam_level); +duob_mode = db_mode(strrep(dataTable.db_mode,'"','')); + +% if database.checkIfRunExists('Results','run_id',run_id) +% disp(['Already got at least one reulst for run id: ',num2str(run_id),' ']) +% return +% end + +% Load and Sync signal data from DB +[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options); + +% Preprocess signal +Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym); + +Scpe_sig.spectrum("fignum",1,"displayname",'Rx') + +%% + +ffe_order = [50, 5, 5]; +mu_ffe = [0.0001, 0.0008, 0.001]; +mu_dfe = 0.0004; +eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^14,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1); +mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',3); + + +%Duobinary Targeting +db_ref_sequence = Duobinary().encode(Symbols); +db_ref_constellation = unique(db_ref_sequence.signal); +[eq_signal, eq_noise] = eq_.process(Scpe_sig,db_ref_sequence); + +%% +if 1 + [mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,Symbols); +else + % Ml MLSE + ml_mlse_equalizer = ML_MLSE("epochs_tr",20,"epochs_dd",1,"len_tr",length(eq_signal),... + "mu_dd",0.01,"mu_tr",0.01,"order",11,"sps",2,... + "traceback_depth",128,"L",1,"delta",4,'adaptive_mu',0); + [mlse_sig_sd,ref_sig] = ml_mlse_equalizer.process(Scpe_sig,db_ref_sequence); +end + +%% +mlse_sig_sd_decoded = Duobinary().decode(mlse_sig_sd,"M",M); +ref_sig_decoded = Duobinary().decode(db_ref_sequence,"M",M); + +mlse_sig_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_sd_decoded); +ref_sig_bits = PAMmapper(M,0,"eth_style",0).demap(ref_sig_decoded); + +err = sum(ref_sig_decoded.signal ~= mlse_sig_sd_decoded.signal); + +[bits_db,errors_db,ber_db,a] = calc_ber(mlse_sig_bits.signal,ref_sig_bits.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); + +%% +switch duob_mode + + case db_mode.no_db + % TX Data is not precoded: + + % A) Emulate diff precoding + mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M); + mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M); + + tx_symbols_precoded = Duobinary().encode(Symbols); + tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); + + tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded); + + [~,errors_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + %B) Just determine BER + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); + [bits_mlse,errors_mlse,ber_db,~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + case db_mode.db_precoded + + % Daten SIND TATSÄCHLICH precoded auf TX Seite: + + % A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here! + mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M); + mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M); + rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd_decoded); + [~,errors_db_diff_precoded,ber_db_diff_precoded,a] = calc_ber(rx_bits_mlse_decoded.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + burst_db_precoded = count_error_bursts(a, 40); + % B) Omit the Coding by comparing with demapped TX symbol sequence + + Tx_bits_ = PAMmapper(M,0,"eth_style",0).demap(Symbols); + rx_bits_mlse = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); + [bits_db,errors_db,ber_db,a] = calc_ber(rx_bits_mlse.signal,Tx_bits_.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + burst_db = count_error_bursts(a, 40); + + cols = linspecer(8); + figure();hold on; + stem(1:40,burst_db,'LineWidth',1,'Color',cols(4,:),'Marker','_','DisplayName','w/o diff. precoder'); + stem(1:40,burst_db_precoded,'LineWidth',1,'Color',cols(3,:),'Marker','.','LineStyle','-','DisplayName','w diff. precoder'); + xlabel('Bit Error Burst Length') + ylabel('Occurence') + set(gca, 'yscale', 'log'); +end + + + + + + +%% SHOW Loss during training + +mu = logspace(-3,-0.8,12); +ber_ml_mlse = zeros(size(mu)); +ber_training = []; +ce_training = []; + +parfor i = 1:numel(mu) + + + + ml_mlse_equalizer = ML_MLSE("epochs_tr",200,"epochs_dd",1,"len_tr",length(Scpe_sig),... + "mu_dd",mu(i),"mu_tr",mu(i),"order",11,"sps",2,... + "traceback_depth",128,"L",2,"delta",4,'adaptive_mu',0); + + [y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Scpe_sig,Symbols); + ref_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ref); + ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + [~, errors, ber_ml_mlse(i), errpos] = calc_ber(ml_mlse_bits.signal, ref_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse(i)); + + ber_training(i,:) = ml_mlse_equalizer.ber; + ce_training(i,:) = ml_mlse_equalizer.ce; +end + +%% +figure();hold on + +plot(mu,ber_ml_mlse,'DisplayName','ML-based MLSE'); + +beautifyBERplot; +xlim([mu(1), mu(end)]); +xlabel('mu'); +ylabel('BER'); +title('PAM-4; M=3; AWGN Channel'); +ylim([1e-5 0.1]); + + +%% +figure() +hold on; +cols = cbrewer2('Spectral',12); +for i = 1:12 + plot(1:200,ber_training(i,:),'DisplayName', sprintf('mu=%.3f ',mu(i)),'Color',cols(i,:)); +end +set(gca,'YScale','log'); +xlabel('Epoch'); +ylabel('BER'); +title('PAM-4; L=3; SNR=20; AWGN Channel'); +plot(1:200,ml_mlse_equalizer_adap.ber,'DisplayName','Adaptive mu'); \ No newline at end of file diff --git a/projects/ML_based_MLSE/interp_fec_cross.m b/projects/ML_based_MLSE/interp_fec_cross.m new file mode 100644 index 0000000..18eac3f --- /dev/null +++ b/projects/ML_based_MLSE/interp_fec_cross.m @@ -0,0 +1,13 @@ +function rop_fec = interp_fec_cross(rops, ber, fec_thr) + if all(~isfinite(ber)) + rop_fec = NaN; return; + end + idx = find(ber < fec_thr, 1, 'first'); + if isempty(idx) || idx == 1 + rop_fec = NaN; return; % no crossing + end + % linear interpolation between the two nearest points + x1 = rops(idx-1); x2 = rops(idx); + y1 = ber(idx-1); y2 = ber(idx); + rop_fec = interp1([y1 y2], [x1 x2], fec_thr, 'linear', NaN); +end diff --git a/projects/ML_based_MLSE/minimal_example_huawei.zip b/projects/ML_based_MLSE/minimal_example_huawei.zip new file mode 100644 index 0000000..cab08ae Binary files /dev/null and b/projects/ML_based_MLSE/minimal_example_huawei.zip differ diff --git a/projects/ML_based_MLSE/minimal_example_huawei/bcjr_pam.m b/projects/ML_based_MLSE/minimal_example_huawei/bcjr_pam.m new file mode 100644 index 0000000..674b55f --- /dev/null +++ b/projects/ML_based_MLSE/minimal_example_huawei/bcjr_pam.m @@ -0,0 +1,555 @@ +classdef bcjr_pam < handle + %MLSE calculates the most probable sequence for an input signal with given/ known channel impulse response of any length + + properties(Access=public) + M %PAM-M + DIR + trellis_states + duobinary_output + end + + methods (Access=public) + + function obj = bcjr_pam(options) + %NAME Construct an instance of this class + % Detailed explanation goes here + + arguments + options.M double = 4; + options.DIR double = [1]; + options.trellis_states double = [-3 -1 1 3]; + options.duobinary_output logical = false; + + end + + % + fn = fieldnames(options); + for n = 1:numel(fn) + try + obj.(fn{n}) = options.(fn{n}); + end + end + end + + function [VITERBI_ESTIMATION_SYMBOLS,LLR_exact,GMI] = process(obj,data_in,data_ref,tx_bits,bit_mapping) + + + debug = 0; + + % States should match the target states of the prev. EQ (EQ's job was to reduce the error between signal and the target) + trellis_state_mode = 2; + % 0 = use provided states (MUST provide the correct states); + % 1 = normalize to = 1 rms; + % 2 = use target symbols; + % 3 = use statistical levels + % 3 analyzes avg of rx signal levels - can help with nonlinear impairments + + trellis_exclusion = 1; % PAM-6 only (only if data is NOT precoded!) + + % Additional scaling between states, expected output (noiseless_received) and the noisy, filtered input signal + scale_mode = 2; % scale_mode: + % 0 = no scaling, + % 1 = use RMS to scale MODEL, + % 2 = use MMSE/time-corr to scale MODEL, -> This best to get the GMI right -> sometimes the LLP's are not centered around zero... + % 3 = use RMS to scale DATA, + % 4 = use MMSE/time-corr to scale DATA + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%% PREPARATIONS %%%%%%%% + + % remove unnecessary zeros at start of impulse response to keep + % number of trellis states minimal + DIR_nonzero = find(obj.DIR ~= 0); + if DIR_nonzero(1) > 1 + obj.DIR(1:DIR_nonzero(1)-1) = []; + end + + if isscalar(obj.DIR) + obj.DIR = [0 obj.DIR]; + end + + % impulse respnse to remove from signal + obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000 + + % Trellis States + obj.trellis_states = reshape(obj.trellis_states,1,[]); + if trellis_state_mode == 1 % Normalize the Trellis states to =1 RMS + + obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states); + + elseif trellis_state_mode == 2 %simply use the states from the ref signal (should be a robust option) + + obj.trellis_states = reshape(unique(data_ref),size(obj.trellis_states)); + + elseif trellis_state_mode == 3 %use_statistical_levels + + %%%% Separate the equalized signal into the respective levels based on the actually transmitted level + constellation = unique(data_ref); + + % find actual levels from rx signal + symbols_for_lvl = NaN(numel(constellation),length(data_ref)); + for l = 1:numel(constellation) + level_amplitude = constellation(l); + symbols_for_lvl(l,data_ref==level_amplitude) = data_in(data_ref==level_amplitude); + end + + %replace the trellis states + avg_levels = mean(symbols_for_lvl,2,'omitnan'); + obj.trellis_states = sort(avg_levels)'; + + %also replace the whole ref signal (PAM-M) levels + [~, idx] = ismember(data_ref, unique(data_ref)); + data_ref = avg_levels(idx); + + end + + + % seems to be the only way to use combvec for a flexible amount + % of vectors. 'combs' contains all trellis states + pre_comb_mat = repmat(obj.trellis_states,length(obj.DIR)-1,1); + pre_comb_cell = mat2cell(pre_comb_mat,ones(1,size(pre_comb_mat,1)),size(pre_comb_mat,2)); + combs = fliplr(combvec(pre_comb_cell{:}).'); + first_sym = combs(:,1); % das ist das älteste/ trailing Symbol aus der sequenz + last_sym = combs(:,end); %hiermit wird entschieden/ das ist das cursor symbol am ende der sequenz + nStates = length(last_sym); + + % % Calculate all possible input symbols for the desired impulse + % % response. Row number is the index of the previous state, + % % column number is the index of the next state + % % noise free received == branch metrics + % assumes: last_sym = combs(:,end); % already defined earlier + levels = sort(unique(obj.trellis_states(:)).'); + edges = [levels(1) levels(end)]; % edge levels (0 and 5 in PAM6) + + noise_free_received = inf(nStates,nStates); % rows: to, cols: from + edge_edge_mask = false(nStates,nStates); % rows: to, cols: from + + for from = 1:nStates + for to = 1:nStates + % valid transition if shift-register overlap holds + if all(combs(to,2:end) == combs(from,1:end-1)) + % noiseless sample for the 'to' state reached from 'from' + noise_free_received(to,from) = ... + dot(combs(to,:), obj.DIR(end:-1:2)) + last_sym(from)*obj.DIR(1); + + % mark edge→edge candidate (to be excluded only on even→odd steps) + edge_edge_mask(to,from) = ... + (last_sym(from)==edges(1) || last_sym(from)==edges(2)) && ... + (last_sym(to) ==edges(1) || last_sym(to) ==edges(2)); + end + end + end + + h = flip(obj.DIR(:)).'; + data_in = data_in(:); + y_ideal = conv(data_ref(:), h, "same"); + + switch scale_mode + case 0 + g = 1; b = 0; + case 1 % RMS: scale model to data + g = rms(data_in)/rms(y_ideal); b = mean(data_in) - g*mean(y_ideal); + case 2 % MMSE/time-corr: scale states to data + [c,lags] = xcorr(data_in(:), y_ideal, 64); + [~,ix] = max(abs(c)); + lag = lags(ix); + y_ideal = circshift(y_ideal, lag); + mu_y = mean(data_in(:)); + mu_i = mean(y_ideal); + y_c = data_in(:)-mu_y; + yi_c = y_ideal-mu_i; + g = (yi_c'*y_c)/(yi_c'*yi_c); + b = mu_y - g*mu_i; + case 3 % RMS flipped: scale data to model + gd = rms(y_ideal)/rms(data_in); bd = mean(y_ideal) - gd*mean(data_in); + data_in = gd*data_in + bd; + g = 1; b = 0; + case 4 % MMSE/time-corr flipped: scale data to states + [c,lags] = xcorr(data_in(:), y_ideal(:), 64); + [~,ix] = max(abs(c)); + lag = lags(ix); + y_ideal = circshift(y_ideal(:), lag); + mu_y = mean(data_in(:)); + mu_i = mean(y_ideal); + y_c = data_in(:) - mu_y; % data_in centered + yi_c = y_ideal - mu_i; % ideal centered + g = (y_c' * yi_c) / (y_c' * y_c); + b = mu_i - g * mu_y; + data_in = g * data_in(:) + b; + g = 1; b = 0; + end + + % apply (g,b) to states/ expected values + noise_free_received = g*noise_free_received + b; + last_sym = g*last_sym + b; + + % calculate noise power + sigma2 = mean(abs(data_in - (g*y_ideal + b)).^2); %noise = mean(abs((RX Signal - IDEAL Signal)))^2 + inv2s2 = 1/(2*sigma2); + + if debug + figure(100); clf; hold on + obj.showLevelScatter_(data_in, data_ref); + yline(noise_free_received(:), 'DisplayName','Transition States','Color','red','HandleVisibility','off'); + yline(obj.trellis_states(:), 'DisplayName','Transition States','Color','green','LineWidth',2,'HandleVisibility','off') + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% FORWARD PASS (VITERBI -Alpha's) %%%%% + + % Initialize the output vector + pm = zeros(nStates,nStates); + bm_fw = zeros(nStates,nStates,length(data_in)); + + % first start is evaluated without ISI/ wihout the full Impulse response + % so simply use the constellation here + bm = -(data_in(1) - last_sym).^2 * inv2s2; + pm = pm + bm; + [alpha(:,1),pm_survivor_fw_idx(:,1)] = max(pm,[],2); + pm = repmat(alpha(:,1).',nStates,1); + bm_fw(:,:,1) = pm; + + % Forward Recursion (FSM Computation) + for n = 2:length(data_in) + + bm = -(data_in(n) - noise_free_received).^2 * inv2s2; + + % exclude edge to edge transitions only for even->odd steps && PAM-6 + if mod(n,2) == 0 && obj.M == 6 && trellis_exclusion + bm(edge_edge_mask) = -Inf; + end + + pm = pm + bm; + [alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as new state (get min distance for all state transitions towards a new state) + pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state) + + bm_fw(:,:,n) = bm; + + end + + % we can now get the best path as min + viterbi_path = NaN(1,length(data_in)); + + % find ideal trellis path by going through the trellis backwards + [~,viterbi_path(length(data_in))] = max(alpha(:,length(data_in))); + for n = length(data_in):-1:2 + viterbi_path(n-1) = pm_survivor_fw_idx(viterbi_path(n),n); + end + + + if debug + alpha_ = alpha - min(alpha) + eps; + figure();hold on; + n = 10; + scatter(1:n,obj.trellis_states(repmat([1:numel(obj.trellis_states)]',1,n)),abs(alpha_(:,end-n+1:end)),'Marker','o','LineWidth',1); + scatter(1:n,obj.trellis_states(viterbi_path(end-n+1:end)),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','green'); + % scatter(1:n,data_ref(end-n+1:end),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','red'); + yticks(obj.trellis_states); + ylim([min(obj.trellis_states)-1 max(obj.trellis_states)+1]); + end + + VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path); + VITERBI_ESTIMATION_SYMBOLS = reshape(VITERBI_ESTIMATION_SYMBOLS,size(data_in)); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% BACKWARD (Beta's) %%%%% + + % Initialize the output vector + pm = zeros(nStates,nStates); + beta = zeros(nStates,length(data_in)); + pm_survivor_bw_idx = zeros(nStates,length(data_in)); + bm_bw = zeros(nStates,nStates,length(data_in)); + + % starting with the state that has the lowest sum path + % metric, follow the stored information about the + % predecessor + for h = length(data_in)-1:-1:1 + + bm = -(data_in(h+1) - noise_free_received).^2 * inv2s2; + + % exclude edge to edge transitions for even->odd steps && PAM-6 + if mod(h+1, 2) == 0 && obj.M == 6 && trellis_exclusion + bm(edge_edge_mask) = -Inf; + end + + pm = pm + bm.'; + [beta(:,h),pm_survivor_bw_idx(:,h)] = max(pm,[],2); % choose lowest path metric as new state + pm = repmat(beta(:,h).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state) + + bm_bw(:,:,h) = bm; + + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% FORWARD (Combine Alpha and Beta to yield LLP's) %%%%% + + %calc the log probabilities (llp's) + + for k = 1:length(data_in) + + if k == 1 + + alpha_ = repmat(alpha(:,k)',[nStates,1])'; + beta_ = beta(:,k); + + LLP(:,k) = max(alpha_ + beta_,[],2); + + else + + alpha_ = repmat(alpha(:,k-1)',[nStates,1])'; + gamma_ = bm_fw(:,:,k)'; + beta_ = beta(:,k); + + LLP(:,k) = max(alpha_ + gamma_,[],1) + beta_'; + end + + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% Calc LLR's %%%%% + + % These are interchangeable... + nml_LLP = LLP - max(LLP); %subtract highest value for better numerical stability, LLP's are not always close to zero + expLLP = exp(nml_LLP); + state_prob = expLLP ./ sum(expLLP); % sums to one (or numerically close to one) + + % compute symbol‐posteriors from LLP in the log‐domain: + amax = max(LLP,[],1); + logZ = amax + log(sum(exp(LLP - amax), 1)); + logPstate = LLP - logZ; % still in log‐domain + state_prob = exp(logPstate); % exact, sums to 1 + + if obj.M == 6 + + num_bits = 5; + + % all possible transitions (for now 36, including the "edges" + % of the QAM 32 constellation) + states = [-5 -3 -1 1 3 5]; + pam6transitions = combvec(states,states)'; % pam6transitions = + % [-5 -5; + % -3 -5; + % -1 -5; ... + + [~, idx_sym_1] = ismember(pam6transitions(:,1), states); + [~, idx_sym_2] = ismember(pam6transitions(:,2), states); + pam6ind = [idx_sym_1, idx_sym_2]; + + numPairs = floor(size(LLP,2)/2); + LLR_exact = zeros(numPairs,5); + LLR_maxlogmap = zeros(numPairs,5); + + for k = 1:numPairs + symbol1 = 2*k-1; + symbol2 = 2*k; + + LLP1 = LLP(:,symbol1); + LLP2 = LLP(:,symbol2); + prob1 = state_prob(:,symbol1); + prob2 = state_prob(:,symbol2); + + % All 36 Combinations: M = LLP Symbol 1 + LLP Symbol 2 + Mij = LLP1(pam6ind(:,1)) + LLP2(pam6ind(:,2)); + pij = prob1(pam6ind(:,1)) .* prob2(pam6ind(:,2)); + + % for each of the 5 bits sum exact-probs or max-log + for b = 1:num_bits + idx_sym_1 = bit_mapping(:,b)==1; + idx_bit_1 = bit_mapping(:,b)==0; + + % exact LLR from probabilities + P1 = sum(pij(idx_sym_1)); %prob that bit == 1 + P0 = sum(pij(idx_bit_1)); + LLR_exact(k,b) = log(P1./P0); %ratio by multiplication + + % max-log: + LLR_maxlogmap(k,b) = max( Mij(idx_sym_1) ) - max( Mij(idx_bit_1) ); % ratio by subtraction + end + end + + % GMI calc includes the Tx-bitstream + tx_bits_pam6_reshaped = reshape(tx_bits',5,[])'; % N x 5 + MI = zeros(1, num_bits); + for k = 1:num_bits + + idx_bit_1 = (tx_bits_pam6_reshaped(:,k) == 0); %wo sind die 1en + idx_sym_1 = (tx_bits_pam6_reshaped(:,k) == 1); %wo sind die 0en + + %LLR's for all actually transmitted ones or zeros + llr0 = LLR_exact(idx_bit_1,k); + llr1 = LLR_exact(idx_sym_1,k); + + % Calculate mutual information for bit position k + I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1 + I1 = mean(log2(1 + exp(-llr1))); % exp(-+LLR) = exp(negative) < 1 + MI(k) = 1 - 0.5 * (I0 + I1); + end + + GMI = sum(MI); % Total mutual information per symbol + GMI = GMI/2; % GMI per single symbol not per two symbols + + else + + % Number of symbols and bits per symbol + num_bits = log2(length(obj.trellis_states)); % 2 bits per symbol + + % bit_mapping = PAMmapper(length(obj.trellis_states),0,"eth_style",0).showBitMapping; + + % Initialize LLR storage + LLR_maxlogmap = zeros(length(data_in),num_bits); + LLR_exact = zeros(length(data_in),num_bits); + + % Compute bit-wise LLRs + for bit_idx = 1:num_bits + + % Find indices where bit is 0 and where it is 1 + idx_bit_0 = bit_mapping(:,bit_idx) == 0; + idx_bit_1 = bit_mapping(:,bit_idx) == 1; + + % Sum over log-probabilities + % Max-Log approximation uses the single max LLP value + % instead of sum over all LLP's + LLR_maxlogmap(:,bit_idx) = max(LLP(idx_bit_1,:), [], 1) - max(LLP(idx_bit_0,:), [], 1); + + % Sum probabilities over states for which the bit is 1 and 0, respectively. + P0 = sum(state_prob(idx_bit_0, :),1); + P1 = sum(state_prob(idx_bit_1, :),1); + LLR_exact(:,bit_idx) = log(P1./P0); % N x num_bits + + + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% CALC NGMI %%%%% + + MI = zeros(1, num_bits); + for k = 1:num_bits + + idx_bit_0 = (tx_bits(:,k) == 0); %wo sind die 1en + idx_bit_1 = (tx_bits(:,k) == 1); %wo sind die 0en + + %LLR's for all actually transmitted ones or zeros + llr0 = LLR_exact(idx_bit_0,k); + llr1 = LLR_exact(idx_bit_1,k); + + % mutual information for bit position k + I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1 + I1 = mean(log2(1 + exp(-llr1))); % exp(-+LLR) = exp(negative) < 1 + MI(k) = 1 - 0.5 * (I0 + I1); % assumes equally distributed ones and zeros + end + + GMI = sum(MI); % Total bitwise mutual information + + end + + + if debug + %%% DEBUG PLOT LIKELIHOOD RATIOS %%% + figure(115);clf + subplot(2,1,1) + for bit = 1:num_bits + hold on; + histogram(LLR_exact(:,bit),1000,"DisplayName",sprintf('Actual LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4); + end + legend + + subplot(2,1,2) + for bit = 1:num_bits + hold on; + histogram(LLR_maxlogmap(:,bit),1000,"DisplayName",sprintf('Max Log LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4); + end + legend + + if obj.M == 6 + pairs = reshape(VITERBI_ESTIMATION_SYMBOLS,2,[]).'; + levels = sort(unique(VITERBI_ESTIMATION_SYMBOLS)); + isedge = ismember(pairs, [levels(1) levels(end)]); + isforbidden = sum(isedge,2)==2; + fprintf('Found %d forbidden transitions (even -> odd ; edge -> edge).\n', nnz(isforbidden)); + end + + end + + + end + + function [symbols_for_lvl,avg_for_lvl] = showLevelScatter_(~,eq_signal,ref_symbols) + + figure() + + rx_symbols = eq_signal; %./ rms(eq_signal); + correct_symbols = ref_symbols; + + % col = cbrewer2('Paired',numel(unique(correct_symbols))*2); + col = ... + [0.6510 0.8078 0.8902; ... + 0.1216 0.4706 0.7059; ... + 0.6980 0.8745 0.5412; ... + 0.2000 0.6275 0.1725; ... + 0.9843 0.6039 0.6000; ... + 0.8902 0.1020 0.1098; ... + 0.9922 0.7490 0.4353; ... + 1.0000 0.4980 0; ... + 0.7922 0.6980 0.8392; ... + 0.4157 0.2392 0.6039; ... + 1.0000 1.0000 0.6000; ... + 0.6941 0.3490 0.1569; ... + 0.6510 0.8078 0.8902; ... + 0.1216 0.4706 0.7059; ... + 0.6980 0.8745 0.5412; ... + 0.2000 0.6275 0.1725]; + ccnt = -1; + + levels = unique(correct_symbols); + symbols_for_lvl = NaN(numel(levels),length(correct_symbols)); + start = 1; + ende = length(correct_symbols); + + for l = 1:numel(levels) + ccnt = ccnt+2; + + level_amplitude = levels(l); + + symbols_for_lvl(l,correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude); + std_lvl(l) = std(symbols_for_lvl(l,:),'omitnan'); + xax = 1:length(correct_symbols); + + scatter(xax(start:ende),symbols_for_lvl(l,start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:)); + hold on; + + + end + + std_lvl = round(std_lvl,2); + + ccnt = 0; + avg_for_lvl = NaN(numel(levels),length(correct_symbols)); + % Add the windowed/ smoothed curves + for l = 1:numel(levels) + ccnt = ccnt+2; + level_amplitude = levels(l); + + L = 500; + movmean = 1/L .* movsum(rx_symbols(correct_symbols==level_amplitude),[L/2,L/2], 'Endpoints', 'fill'); + + avg_for_lvl(l,correct_symbols==level_amplitude) = movmean; + + nanx = isnan(avg_for_lvl(l,:)); + t = 1:numel(avg_for_lvl(l,:)); + avg_for_lvl(l,nanx) = interp1(t(~nanx), avg_for_lvl(l,~nanx), t(nanx)); + + plot(xax(start:ende),avg_for_lvl(l,start:ende),'Color',col(ccnt,:)); + + hold on + end + + % yline(levels); + xlabel('Samples'); + ylabel('Amplitude'); + ylim([-3 3]); + + end + + + end +end diff --git a/projects/ML_based_MLSE/minimal_example_huawei/minimal_example.m b/projects/ML_based_MLSE/minimal_example_huawei/minimal_example.m new file mode 100644 index 0000000..504a1b2 --- /dev/null +++ b/projects/ML_based_MLSE/minimal_example_huawei/minimal_example.m @@ -0,0 +1,193 @@ + +if 0 + % A) RUN FULL LOOP + M_format = [2,4,6,8]; + snr = 10:25; +else + % B) RUN FOR DEBUG AND TEST + M_format = 4; + snr = 20; +end + +for m = 1:length(M_format) + % --- Parameters --- + M = M_format(m); % PAM order (e.g., 2,4,8) + Nsym = 1e5; % number of symbols + h = [1, 0.5, 0.2]; % Impulse response to remove + + b = log2(M); + if M == 6 b = 5; end + rng(1); + bits_tx = logical(randi([0 1], Nsym, b, 'uint8')); + + tx_symbols = pammap(bits_tx,M); + + if M == 6 + states = unique(tx_symbols); + pam6transitions = combvec(states',states')'; % pam6transitions = + bitmapping = pamdemap(reshape(pam6transitions',1,[])',M); + else + bitmapping = pamdemap(unique(tx_symbols),M); + end + + scaling = sqrt(sum(unique(tx_symbols).^2)/numel(unique(tx_symbols))); + tx_symbols = tx_symbols ./ scaling; + + % apply impulse response to signal + y_filt = filter(h, 1, tx_symbols); + + for s = 1:length(snr) + + % apply noise + y = awgn(y_filt,snr(s),"measured",1); + + % apply ml-MLSE + adaptive_mu = 0; + mu_lms = 0.15; + ml_mlse_equalizer = ml_mlse_pam("epochs_tr",50,"epochs_dd",1,"len_tr",length(y)/2,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... + "L",2,"delta",4,"adaptive_mu",adaptive_mu); + + [ml_mlse_estimate,~] = ml_mlse_equalizer.process(y,tx_symbols); + rx_symbols = ml_mlse_estimate .* scaling; + bits_rx = pamdemap(rx_symbols,M); + + BER_ml(m,s) = nnz(bits_tx ~= bits_rx) / numel(bits_tx); + fprintf('BER = %.2e \n', BER_ml(m,s)); + + + % apply bcjr + BCJR = bcjr_pam("DIR",h,"duobinary_output",0,"M",M,"trellis_states",unique(tx_symbols)); + [viterbi_estimate,LLR,GMI(m,s)] = BCJR.process(y,tx_symbols,bits_tx,bitmapping); + + % decode LLR's + bits_LLR = LLR > 0; + + % demap viterbi symbols sequence + rx_symbols = viterbi_estimate .* scaling; + bits_rx = pamdemap(rx_symbols,M); + + % BER calc + BER_vit(m,s) = nnz(bits_tx ~= bits_LLR) / numel(bits_tx); + fprintf('BER LLR = %.2e \n', BER_vit(m,s)); + + BER_llr(m,s) = nnz(bits_tx ~= bits_rx) / numel(bits_tx); + fprintf('BER = %.2e \n', BER_llr(m,s)); + end +end +%% +figure();hold on +for m = 1:length(M_format) + p=plot(snr,BER_llr(m,:),'DisplayName',sprintf('Viterbi: PAM %d',M_format(m))); + plot(snr,BER_ml(m,:),'DisplayName',sprintf('ML-Based: PAM %d',M_format(m)),'LineStyle',':','Color',p.Color); +end +ylabel('BER'); +xlabel('SNR') +title('BER vs. SNR'); +set(gca, 'XScale', 'linear', ... + 'YScale', 'log', ... + 'TickLabelInterpreter', 'latex', ... + 'FontSize', 11); + +%% +figure();hold on +for m = 1:length(M_format) + plot(snr,GMI(m,:),'DisplayName',sprintf('GMI PAM %d',M_format(m))) +end +ylabel('GMI'); +xlabel('SNR') +title('GMI vs. SNR'); +set(gca, 'XScale', 'linear', ... + 'YScale', 'linear', ... + 'TickLabelInterpreter', 'latex', ... + 'FontSize', 11); + +function symbols = pammap(bits,M) +bits = logical(bits); +if M == 2 + symbols = bits; +elseif M == 4 + symbols= 2*bits(:,1) + (bits(:,1)==bits(:,2)); + symbols=2*symbols-3; + +elseif M == 6 + + m = 1; + + if size(bits,2)>size(bits,1) + bits = bits'; %vector aufrecht stellen + end + bits = reshape(bits',1,[])'; + thres = [-3 5;-1 5;-3 -5;-1 -5;-5 3;-5 1;-5 -3;-5 -1;-1 3;-1 1;-1 -3;-1 -1;-3 3;-3 1;-3 -3;-3 -1;3 5;1 5;3 -5;1 -5;5 3;5 1;5 -3;5 -1;1 3;1 1;1 -3;1 -1;3 3;3 1;3 -3;3 -1]; + % LUT based mapping + for k = 1:5:fix(length(bits)/5)*5 + symbols(m:m+1,1) = thres(bin2dec(int2str(bits(k:k+4)'))+1,:); + m = m+2; + end + +elseif M == 8 + x1 = bits(:,1); + x2 = (bits(:,1)==bits(:,3)); + x3 = x2~=bits(:,2); + + symbols = 4*x1 + 2*x2 + x3; + symbols=2*symbols-7; +end +end + +function bits = pamdemap(symbols,M) + +if M == 2 + thres=0; +elseif M == 4 + thres=[-2,0,2]; +elseif M == 6 + thres = [-3 5;-1 5;-3 -5;-1 -5;-5 3;-5 1;-5 -3;-5 -1;-1 3;-1 1;-1 -3;-1 -1;-3 3;-3 1;-3 -3;-3 -1;3 5;1 5;3 -5;1 -5;5 3;5 1;5 -3;5 -1;1 3;1 1;1 -3;1 -1;3 3;3 1;3 -3;3 -1]; +elseif M == 8 + thres=-6:2:6; +end + +if M ~= 6 + symbols = symbols'; + a = squeeze(repmat(real(symbols),[1 1 length(thres)])); %Eingangssignal in 3 spalten + b = squeeze(repmat(reshape(thres(:).',[1 1 length(thres)]),[1 length(symbols) 1])); %Threshold in 3 Spalten + comp_real = a > b; %check for each symbol/ sampling if it exeeds the obj.thresholdseshold 1, 2 or 3 + comp_real=repmat(real(symbols),[1 1 length(thres)]) > repmat(reshape(thres(:).',[1 1 length(thres)]),[1 length(symbols) 1]); + s1=size(comp_real,1); + s2=size(comp_real,2); +end + +if M == 2 + data_out=abs(comp_real(:,:,1)); +elseif M == 4 + data_out=[comp_real(:,:,2); ones(s1,s2) - comp_real(:,:,1) + comp_real(:,:,3)]; +elseif M == 6 + + if size(symbols,2) > 1 + symbols = symbols.'; + end + + if length(symbols)/2 ~= round(length(symbols)/2) + symbols = [symbols;0]; + end + + m = 1; + for n = 1:2:length(symbols) + dist = sqrt((symbols(n)-thres(:,1)).^2+(symbols(n+1)-thres(:,2)).^2); + [~,dd_idx] = min(dist); + % dec_out(n:n+1) = LUT(dd_idx,:); + data_out(m:m+4) = bitget(dd_idx-1,5:-1:1); + m = m+5; + end + + data_out = reshape(data_out',5,[]); + +elseif M == 8 + data_out=[comp_real(:,:,4); + comp_real(:,:,1)-comp_real(:,:,3)+comp_real(:,:,5)-comp_real(:,:,7); + 1-comp_real(:,:,2)+comp_real(:,:,6)]; +end + +bits = data_out'; + +end \ No newline at end of file diff --git a/projects/ML_based_MLSE/minimal_example_huawei/ml_mlse_pam.m b/projects/ML_based_MLSE/minimal_example_huawei/ml_mlse_pam.m new file mode 100644 index 0000000..84ebfb4 --- /dev/null +++ b/projects/ML_based_MLSE/minimal_example_huawei/ml_mlse_pam.m @@ -0,0 +1,473 @@ +classdef ml_mlse_pam < handle + + % ALGORITHM DESCRIBED IN: + % W. Lanneer and Y. Lefevre, “Machine Learning-Based Pre-Equalizers for + % Maximum Likelihood Sequence Estimation in High-Speed PONs,” + % in 2023 31st European Signal Processing Conference + + % Further ML Refs: + % https://machinelearningmastery.com/cross-entropy-for-machine-learning/ + % https://docs.pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html + + % The central idea is to overcome the (white-) noise assumption within the previously described + % Viterbi algorithm, more precisely a closed-loop optimization is proposed that finds a suitable + % filter-set to directly compute the branch metrics c_k (s,s^' ). These can directly be used to + % carry out the conventional Viterbi algorithm. The system consists of S^L S=F linear FIR filters, + % combined with one bias coefficient respectively. These filters take the received input samples to + % compute the branch metrics estimates (c_k ) ̂(s,s^' ) according toThe central idea is to overcome + % the (white-) noise assumption within the previously described Viterbi algorithm, more precisely + % a closed-loop optimization is proposed that finds a suitable filter-set to directly compute the + % branch metrics c_k (s,s^' ). These can directly be used to carry out the conventional Viterbi + % algorithm. The system consists of S^L S=F linear FIR filters, combined with one bias coefficient + % respectively. These filters take the received input samples to compute the branch metrics + % estimates. Finally, the usual Viterbi is carried out... + + % Recommended Settings and some findings: + + % Requires many training epochs. According to ML people, 100,200 or + % even up to 1000 epochs are normal for ML-convergence + + % The mu parameter _can_ be adaptive - using the cross entropy and when + % analyzing the isolated training it looks very promisig. However, is + % later use I found this is not as stable as a fixed learning rate. + % mu = 0.1 worked good for me + + % Longer orders/ filter length are not always better. For me order=11 + % was good. + + % Delay factor (delta) is good when the order is also increased. With + % order = 11, a delta of =4 shows good results + + properties + sps % usually 2 + order + e + e_tr + error + + len_tr + mu_tr + epochs_tr + + % dd_mode -> not implemented here! + mu_dd %weight update in dd mode + epochs_dd + + adaptive_mu + + constellation + + L %viterbi memory length + + alpha + DIR + DIR_flip + trellis_states + + traceback_depth + + S + Nf + delta + nStates + nFeasible + combs + first_sym + last_sym + valid + valid_to_idx + valid_from_idx + w + nbiasTerms + + true_to_state_idx + state_dict % containers.Map: key(sequence)->state index + key_fmt = '%.8g_'; % key format for sequence strings + nSym % |constellation| + + ber = [] + ce = ones(1,1); + end + + methods + function obj = ml_mlse_pam(options) + arguments(Input) + + options.sps = 2; + options.order = 15; + + options.len_tr = 4096; + options.mu_tr = 0; + options.epochs_tr = 5; + + % options.dd_mode = 1; + options.mu_dd = 1e-5; + options.epochs_dd = 5; + + options.adaptive_mu = 1; + + options.delta = 0; + options.traceback_depth = 1024; + + options.L = 1 + + end + + fn = fieldnames(options); + for n = 1:numel(fn) + obj.(fn{n}) = options.(fn{n}); + end + + obj.e = zeros(obj.order,1); + obj.error = 0; + end + + function [x_viterbi,x_ref] = process(obj, X, D) + + % actual processing of the signal (steps 1. - 3.) + % 1 normalize RMS + X = X./rms(X); + + % Use sorted constellation for deterministic mapping + obj.constellation = sort(unique(D),'ascend'); + obj.nSym = numel(obj.constellation); + + if length(X)/length(D) ~= obj.sps + warning('Signal length does not fit to reference!'); + end + + % ============================================================== + % INITIALIZATION + % ============================================================== + + % --- Parameters + obj.S = numel(obj.constellation); % Num of Symbols + obj.Nf = obj.order*obj.sps; % filter length (auto adapt for n-SPS...) + obj.nStates = obj.S^obj.L; % S^L states + obj.nFeasible = obj.nStates*obj.S; % S^(L+1) feasible states + + % --- Trellis mapping + obj.trellis_states = reshape(obj.constellation,1,[]); % make row vector + pre_comb_mat = repmat(obj.trellis_states, obj.L, 1); + pre_comb_cell = mat2cell(pre_comb_mat, ones(1,obj.L), size(pre_comb_mat,2)); + obj.combs = fliplr(combvec(pre_comb_cell{:}).'); % rows: states, columns: [x_k, x_{k-1}, ...] + obj.first_sym = obj.combs(:,1); + obj.last_sym = obj.combs(:,end); + obj.nStates = size(obj.combs,1); + + % --- Valid transitions; adapted from the old Viterbi in + % Move-It where the "noise free received" states are calculated + % using the same loop and clause + obj.valid = false(obj.nStates); + for from = 1:obj.nStates + for to = 1:obj.nStates + if all(obj.combs(to,2:end) == obj.combs(from,1:end-1)) + obj.valid(to,from) = true; + end + end + end + [obj.valid_to_idx, obj.valid_from_idx] = find(obj.valid); + + % Allocate vectors and weights + % !! IF SHAPE FIT, then we already have smth there an we want + % to start with the existing filter-set (saves comp. time/ or to test fixed filter on new data) + obj.nbiasTerms = 1; + if isempty(obj.w) || any(size(obj.w) ~= [obj.Nf+obj.nbiasTerms,obj.nFeasible]) + obj.w = zeros(obj.Nf+obj.nbiasTerms,obj.nFeasible); % filter weights per transition + bias tap + % obj.w = randn(obj.Nf+obj.nbiasTerms,obj.nFeasible); + end + + % This is a weird workaround - but it works and is much faster + % than findig the state indices every time: + % Precompute dictionary for fast state lookup (sequence -> state) + keys = cell(obj.nStates,1); + for i = 1:obj.nStates + keys{i} = obj.seq_key(obj.combs(i,:)); % combs row is already [x_k, x_{k-1}, ...] + end + obj.state_dict = containers.Map(keys, 1:obj.nStates); + + % ============================================================== + % TRAINING + % ============================================================== + + n = obj.len_tr; + training = 1; + obj.equalize(X, D,obj.mu_tr,obj.epochs_tr,n,training); + obj.e_tr = obj.e; + + % ============================================================== + % Testing; Fixed Mode + % ============================================================== + + n = length(X); + training = 0; + obj.mu_dd = obj.mu_tr; %For now no DD mode is implemented... + [x_viterbi,x_ref]=obj.equalize(X, D,obj.mu_dd,obj.epochs_dd,n,training); + + end + + function [y,y_ref] = equalize(obj,x,d,mu,epochs,N,training) + % ============================================================== + % ML-Based Branch Metric Estimation + Viterbi + % ============================================================== + debug = 1; + showPlots = 1; + + nSymbols = ceil(N/obj.sps); + + for epoch = 1:epochs + + % state metrics (log-domain costs): keep as column [nStatesx1] + pm = zeros(obj.nStates,1); + v_tilde = zeros(1,obj.nFeasible); + pred = zeros(nSymbols, obj.nStates); + pm_sto = nan(obj.nStates, nSymbols); + CE_accum = 0; + + % START IDX can be randomized during training, but this + % requires some testing - it is not better, maybe a + % solutiuon is to use the same window for 10-20 epochs + % and then switch to another window + % for now: simply use the first parts of the signal for + % training and also for testing... not "the + randomize_training_window = 0; + if randomize_training_window && training + max_start = length(x) - ( (ceil(N/obj.sps)-1)*obj.sps + 1 ); + max_start = max(1, max_start); % safety + start_sample = randi([1, max_start], 1); %rnd training; not really good + else + start_sample = 1; + end + + end_sample = start_sample + (ceil(N/obj.sps)-1)*obj.sps; + start_symbol = 1 + floor((start_sample - 1)/obj.sps); % ABSOLUTE symbol index + + symbol = 0; + for sample = start_sample:obj.sps:end_sample + symbol = symbol + 1; + k = symbol; + sym_idx = start_symbol + (symbol - 1); + + % input signal window y_k; delayed by delta + i1 = sample - obj.Nf + 1 + obj.delta; + i2 = sample + obj.delta; + buf = x(max(1,i1):min(length(x),i2)); + padL = max(0,1 - i1); + padR = max(0,i2 - length(x)); + yk = [zeros(padL,1); buf(:); zeros(padR,1)]; % Nfx1 + yk = [yk;ones( obj.nbiasTerms,1)]; + + % Apply Filter; Predict branch metrics for all feasible transitions: c_hat + % Formula (8) + c_hat = (yk.' * obj.w); % [1xnFeasible] + c_hat = c_hat.'; % [nFeasiblex1] + + % Extended path metrics: v_tilde = pm(from) + c_hat + v_tilde = pm(obj.valid_from_idx) + c_hat; % [nFeasiblex1] + + % ===== Cross Entropy Loss Update ===== + + if 1 %training + % --- allocate storage once + if epoch == 1 && symbol == 1 + obj.true_to_state_idx = ones(ceil(N/obj.sps),1,'uint32'); + end + + % --- previous "to" becomes current "from" + if symbol > 1 + true_from_state_idx = obj.true_to_state_idx(symbol-1); + else + true_from_state_idx = 1; + end + + % --- compute or reuse "to" state + if epoch == 1 + % only compute in first epoch + if sym_idx >= obj.L + key_to = obj.seq_key(flip(d(sym_idx-obj.L+1 : sym_idx))); + if isKey(obj.state_dict, key_to) + obj.true_to_state_idx(symbol) = obj.state_dict(key_to); + else + obj.true_to_state_idx(symbol) = true_from_state_idx; + end + else + obj.true_to_state_idx(symbol) = true_from_state_idx; + end + end + + % --- ensure valid (from,to) + dirac = zeros(obj.nFeasible,1); + mask = obj.valid_from_idx==true_from_state_idx & ... + obj.valid_to_idx == obj.true_to_state_idx(symbol); + if any(mask) + dirac(mask) = 1; + else + idx = find(obj.valid_from_idx==true_from_state_idx,1,'first'); + dirac(idx) = 1; + obj.true_to_state_idx(symbol) = obj.valid_to_idx(idx); + end + + % softmax over -v_tilde (numerically safe shift) + v_shift = -(v_tilde - min(v_tilde)); % shift to small positive numbers + v_shift = min(v_shift, 100); % clamp exponent argument to avoid extreme numbers/ overflow (exp(50)=5e21) + expv = exp(v_shift); + p = expv ./ (sum(expv) + eps); + + % Cross entropy + CE_symbol(symbol) = -log(p(dirac==1) + eps); + + if sym_idx > obj.L + CE_smooth(symbol) = 0.01*CE_symbol(symbol) + 0.99*CE_smooth(symbol-1); + else + if epoch > 1 + CE_smooth(symbol) = obj.ce(end); %stitch together ce from last epoch? or =1 for very first round?! + else + CE_smooth(symbol) = CE_symbol(symbol); + end + end + + CE_accum = CE_symbol(symbol) + CE_accum; + + % Formula (10) + % gradient term (t - p) + dmp = (dirac - p)'; % 1xnFeasible + + % Formula (10) + dL_Dw = (yk) .* dmp; + + % Start updates only when the symbol index has ≥ L history + if sym_idx >= obj.L + if obj.adaptive_mu + mu_eff = CE_smooth(sym_idx); + mu_eff = max(min(mu_eff, 0.2), 1e-4); + else + mu_eff = mu; + end + + % see Algorithm 1 in paper + obj.w = obj.w - mu_eff .* dL_Dw; % (Nf+1)xnFeasible + end + + % if debug && epoch > 2 + % figure(100); + % subplot(4,1,1); + % heatmap(p'); + % title('Probs') + % subplot(4,1,2); + % heatmap(dmp); + % title('Update') + % subplot(4,1,3); + % heatmap(dL_Dw); + % title('Update') + % subplot(4,1,4); + % heatmap(bj.w); + % title('Update') + % + % end + + end + + % Compare-Select + v_tilde_mat = inf(obj.nStates, obj.nStates); + v_tilde_mat(obj.valid) = v_tilde; %reshapes to usual (from x to) matrix + [pm_next, pred(k,:)] = min(v_tilde_mat, [], 2); %here, calc min for each column + + % re-center, otherwise it will overflow + pm_next = pm_next - min(pm_next); + + pm = pm_next; + pm_sto(:,symbol) = pm; + end + + % Traceback + [~, s_end] = min(pm); + viterbi_path = zeros(symbol,1); + viterbi_path(symbol) = s_end; + for n = symbol:-1:2 + viterbi_path(n-1) = pred(n, viterbi_path(n)); + end + + % cut here to have the same indices when shuffling/ + % starting the start_symbol indx != 1 + y_ref = d(start_symbol:end); + y = obj.first_sym(viterbi_path); + + % Debug and Plots + if debug && training + sym_start = start_symbol; + sym_end = start_symbol + symbol - 1; + ref_slice = d(sym_start : sym_end); + err = sum(y ~= ref_slice(1:numel(y))); + + try %works with demapper, not provided in Deliverable + ref_bits = PAMmapper(obj.S,0).demap(ref_slice); + eq_bits = PAMmapper(obj.S,0).demap(y); + [~, ~, ber, ~] = calc_ber(ref_bits, eq_bits, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('Epoch: %d - BER: %.1e \n',epoch, ber); + obj.ber(epoch) = ber; + berlabel = 'BER'; + catch %fallback ser + ser = err./length(y); + fprintf('Epoch: %d - SER: %.1e \n',epoch, ser); + obj.ber(epoch) = ser; + berlabel = 'BER'; + end + + obj.ce(epoch) = CE_accum./symbol; + + if showPlots + figure(10);clf + subplot(3,2,1:2); + heatmap(obj.w); + title('Filter') + + subplot(3,2,3); + v_tildemat = NaN(obj.nStates, obj.nStates); + v_tildemat(obj.valid) = v_tilde; % log-domain scores + heatmap(v_tildemat); + title('Extended Path Metrics v-tilde') + + subplot(3,2,4); + scatter(1:symbol,pm_sto,1,'.') + title('Path Metric Winners v') + + subplot(3,2,5);hold on + scatter(1:symbol,CE_symbol,1,'.'); + scatter(1:symbol,CE_smooth,1,'.') + title('Cross Entropy') + ylabel('Cross Entropy') + xlabel('Symbols') + + subplot(3,2,6); hold on + % Left y-axis: Cross Entropy + yyaxis left + scatter(1:length(obj.ce), obj.ce, 10, 's', 'filled') + ylabel('Cross Entropy') + + % Right y-axis: BER + yyaxis right + scatter(1:length(obj.ber), obj.ber, 10, 'd', 'filled') + set(gca, 'YScale', 'log') + ylabel(berlabel) + + xlim([1, epochs]) + xlabel('Epoch') + title('Cross Entropy // BER') + grid on + + drawnow + end + end + end + end + end + + methods (Access=private) + function k = seq_key(obj, seq) + % Build a stable key string for a sequence row vector in the *same order as combs rows* ([x_k, x_{k-1}, ...]) + % Use rounding via sprintf to avoid floating-point issues. + % seq must be a row vector. + k = sprintf(obj.key_fmt, seq); + end + end +end diff --git a/projects/ML_based_MLSE/model.m b/projects/ML_based_MLSE/model.m new file mode 100644 index 0000000..4610b72 --- /dev/null +++ b/projects/ML_based_MLSE/model.m @@ -0,0 +1,222 @@ +%%% Run parameters +% TX +M = 4; + +apply_pulsef = 1; +fdac = 256e9; +fadc = 256e9; +random_key = 2; + +rcalpha = 0.05; +kover = 8; +vbias_rel = 0.5; +u_pi = 3.2; +vbias = -vbias_rel*u_pi; +laser_wavelength = 1310; +laser_linewidth = 1e6; + +% Channel +link_length = 0; + + +doub_mode = db_mode.no_db; +cols = linspecer(6); +rop = [-8]; +bwl = [0.5:0.1:1.5]; +fsym = [160:16:256].*1e9; +% nonlin_mod = [0.5:0.01:0.75]; +nonlin_mod = ones(size(fsym)).*0.5; + +ffe_results = {}; +mlse_results_lin= {}; + +for r = 1:length(fsym) + + Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha); + + db_precode = 0; + db_encode = 0; + duob_mode = db_mode.no_db; + apply_pulsef = 1; + + [Digi_sig,Symbols,Tx_bits] = PAMsource(... + "fsym",fsym(r),"M",M,"order",18,"useprbs",0,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.5,... + "applypulseform",apply_pulsef,"pulseformer",Pform,... + "randkey",random_key,... + "db_precode",db_precode,"db_encode",db_encode,... + "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); + + El_sig = M8199B("kover",kover).process(Digi_sig); + + %%%%% Electrical Driver Amplifier %%%%%% + El_sig = El_sig.normalize("mode","oneone"); + + %%%%% MODULATE E/O CONVERSION %%%%% + u_pi = 3.2; + vbias = -u_pi*nonlin_mod(r); + [Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1).process(El_sig); + + %%%%%% Fiber %%%%%% + Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); + + %%%%%% ROP %%%%%% + Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); + + %%%%%% PD Square Law %%%%%% + PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",random_key).process(Opt_sig); + + %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% + rx_bwl = 70e9; + PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig); + + % %%%%%% Low-pass Scope %%%%%% + Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + + %%%%%% Scope %%%%%% + 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',1,'H_lpf',Lp_scpe).process(PD_sig); + + Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym(r)); + + % 2sps + [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 1); + Rx_sig_2sps = Scpe_cell{1}; + Rx_sig_2sps = Rx_sig_2sps.normalize("mode","rms"); + + % 1sps + Scpe_sig_1sps = Scpe_sig.resample("fs_out",1*fsym(r)); + + [~, Scpe_cell_1sps, ~, found_sync] = Scpe_sig_1sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 1); + Rx_sig_1sps = Scpe_cell_1sps{1}; + Rx_sig_1sps = Rx_sig_1sps.normalize("mode","rms"); + showLevelHistogram(Rx_sig_1sps,Symbols,"displayname",'ffe','fignum',111); + + + %% + + mu_lms = 0.0005; + pf_ncoeffs = 2; + eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms"); + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + + % FFE + [y_ffe, ffe_noise] = eq_.process(Rx_sig_2sps, Symbols); + + Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ffe); + [~, errors, ber_ffe, ~] = calc_ber(Eq_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); + fprintf('FFE: %.2e \n',ber_ffe); + + % Postfilter + [y_white,whitened_noise] = 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_mlse_normal, errpos] = calc_ber(mlse_bits.signal, Tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('MLSE: %.2e \n',ber_mlse_normal); + + showLevelHistogram(y_ffe,Symbols,"displayname",'ffe','fignum',111); + + bursts = count_error_bursts(errpos, 10); + e = zeros(size(mlse_bits.signal)); + e(errpos) = 1; + figure(8) + stem(e) + + %% RUN ML-Based MLSE + + mu_lms = 0.15; + ml_mlse_equalizer = ML_MLSE("epochs_tr",50,"epochs_dd",10,"len_tr",Rx_sig_2sps.length-100,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",15,"sps",2,... + "traceback_depth",128,"L",3,"delta",5); + + %% + ml_mlse_equalizer.epochs_tr = 50; + ml_mlse_equalizer.epochs_dd = 1; + [y_ml_mlse,Vit_signal] = ml_mlse_equalizer.process(Rx_sig_2sps,Symbols); + ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + [~, errors, ber, errpos] = calc_ber(ml_mlse_bits.signal, Tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('ML MLSE BER: %.2e \n',ber); + + bursts = count_error_bursts(errpos, 10); + e = zeros(size(ml_mlse_bits.signal)); + e(errpos) = 1; + figure(8) + stem(e) + + figure() + plot(ml_mlse_equalizer.ber) + beautifyBERplot + + + + + + + %% optimize delta + deltas = [-1:4]; + ber = zeros(1,length(deltas)); + parfor m = 1:numel(deltas) + + mu_lms = 0.2; + ml_mlse_equalizer = ML_MLSE("epochs_tr",2,"epochs_dd",5,"len_tr",2^13,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",4,"sps",1,... + "traceback_depth",128,"L",3,"delta",deltas(m)); + + [y_ml_mlse,Vit_signal] = ml_mlse_equalizer.process(y_ffe,Symbols); + y_ml_mlse_ = y_ml_mlse; + y_ml_mlse_.signal = circshift(y_ml_mlse.signal,0); + mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse_); + [~, errors, ber(m), errpos] = calc_ber(mlse_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); + fprintf('ML MLSE: %.2e \n',ber(m)); + end + + + figure(7); hold on + title('ML MLSE') + plot(deltas,ber,'DisplayName','BER'); + yline(ber_ffe,'DisplayName','BER FFE'); + yline(ber_mlse_normal,'DisplayName','BER MLSE'); + xlabel('deltas') + beautifyBERplot + legend + ylim([1e-5, 1e-1]); + set(gca,'YScale','log'); + + + + + %% RUN Comparison + len_tr = 4096*2; + + mu_ffe1 = 0.0001; + mu_ffe2 = 0.0008; + mu_ffe3 = 0.001; + mu_dc = 0.005; + % mu_dc = 0; + + mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3]; + mu_dfe = 0.0004; + pf_ncoeffs = 1; + ffe_order = [50, 0, 0]; + mu_lms = 0.0005; + eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms"); + % eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",1,"DCmu",0.00,"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).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0); + + [ffe_results{r}, mlse_results_lin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig_2sps, Symbols, Tx_bits, ... + "precode_mode", duob_mode,... + 'showAnalysis', 0, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + mlse_results_lin{r}.metrics.print; + ffe_results{r}.metrics.print; + +end \ No newline at end of file diff --git a/projects/ML_based_MLSE/rate_evaluation.m b/projects/ML_based_MLSE/rate_evaluation.m new file mode 100644 index 0000000..00494b7 --- /dev/null +++ b/projects/ML_based_MLSE/rate_evaluation.m @@ -0,0 +1,116 @@ + +ber_ffe = []; +ber_mlse = []; +ber_dbtgt = []; +ber_ml = []; + +mlse = 1; +dbtgt = 0; +duob_mode = db_mode.no_db; +baudrates = [136:8:224].*1e9; +for i = 1:length(baudrates) + + rop = -8; + M = 4; + [Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1] = standard_link_model("M",M,"fsym",baudrates(i),"rop",rop,"laser_linewidth",1310,"link_length_m",0,"random_key",1,"apply_pulsef",1); + % [Rx_sig_2sps_v2, Symbols_v2, Tx_bits_v2] = standard_link_model("M",M,"fsym",200e9,"rop",rop,"laser_linewidth",1300,"link_length_m",0,"random_key",2); + % [Rx_sig_2sps_v3, Symbols_v3, Tx_bits_v3] = standard_link_model("M",M,"fsym",200e9,"rop",rop,"laser_linewidth",1300,"link_length_m",0,"random_key",3); + + %% FFE + MLSE + if mlse + pf_ncoeffs = 1; + ffe_order = [50, 0, 0]; + mu_ffe = [0.0001, 0.0008, 0.001]; + mu_dfe = 0.0004; + eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"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).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients); + + [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1, ... + "precode_mode", duob_mode,... + 'showAnalysis', 0, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + ber_ffe(i) = ffe_results.metrics.BER; + ber_mlse(i) = mlse_results.metrics.BER; + + fprintf('BER FFE: %.2e \n',ber_ffe(i)); + fprintf('BER MLSE: %.2e \n',ber_mlse(i)); + end + + + %% FFE DB tgt. + MLSE + if dbtgt + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',3); + ffe_order = [50, 0, 0]; + mu_ffe = [0.0001, 0.0008, 0.001]; + mu_dfe = 0.0004; + eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1); + + dbt_results = duobinary_target(eq_, mlse_db_, M, Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1, ... + "precode_mode", duob_mode, ... + 'showAnalysis', 0,... + "postFFE", []); + + ber_dbtgt(i) = dbt_results.metrics.BER; + end + + + %% + mu_lms = 0.0005; + pf_ncoeffs = 2; + eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms"); + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + + % FFE + [y_ffe, ffe_noise] = eq_.process(Rx_sig_2sps_v1, Symbols_v1); + + + % Postfilter + [y_white,whitened_noise] = pf_.process(y_ffe, ffe_noise); + + %% RUN ML-Based MLSE + + mu_lms = 0.15; + ml_mlse_equalizer = ML_MLSE("epochs_tr",50,"epochs_dd",1,"len_tr",2^16,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",4,"sps",2,... + "traceback_depth",128,"L",2,"delta",0); + + ml_mlse_equalizer.mu_tr = 0.005; + ml_mlse_equalizer.epochs_tr = 2; + ml_mlse_equalizer.epochs_dd = 1; + [y_ml_mlse,~] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1); + ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + [~, errors, ber_ml(i), errpos] = calc_ber(ml_mlse_bits.signal, Tx_bits_v1.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('ML MLSE BER: %.2e \n',ber_ml(i)); + + % figure(11);hold on + % plot(1:numel(ml_mlse_equalizer.ber),ml_mlse_equalizer.ber); + % beautifyBERplot; + % xlim([1,numel(ml_mlse_equalizer.ber)]) + +end + +%% + +figure(6); hold on; +if mlse +plot(baudrates,ber_ffe,'DisplayName','FFE'); +plot(baudrates,ber_mlse,'DisplayName','MLSE'); +end +if dbtgt +plot(baudrates,ber_dbtgt,'DisplayName','DB tgt'); +end +plot(baudrates,ber_ml,'DisplayName','ML-MLSE'); +beautifyBERplot; +legend + + + + + + + + + diff --git a/projects/ML_based_MLSE/read_csv.m b/projects/ML_based_MLSE/read_csv.m new file mode 100644 index 0000000..fbed164 --- /dev/null +++ b/projects/ML_based_MLSE/read_csv.m @@ -0,0 +1,30 @@ +%% read_wpd_csv.m +% Minimal importer for WebPlotDigitizer multi-curve CSV + +filename = 'wpd_datasets.csv'; % <-- set your file path here +T = readtable(filename); + +% Read header row manually +fid = fopen(filename); +hdr1 = strsplit(strrep(fgetl(fid), '"', ''), ','); % curve names +hdr2 = strsplit(strrep(fgetl(fid), '"', ''), ','); % X/Y header row +fclose(fid); + +% Extract unique curve names +names = hdr1(~cellfun('isempty',hdr1)); + +% Create struct for each curve +mii = struct(); +for i = 1:numel(names) + base = matlab.lang.makeValidName(strrep(names{i},' ','_')); + xi = 2*(i-1)+1; % X column + yi = xi+1; % Y column + mii.(base).X = T{:,xi}; + mii.(base).Y = T{:,yi}; + + % also create workspace variable "name_wpd" + assignin('base',[base '_wpd'], mii.(base)); +end + +disp('Imported datasets:'); +disp(fieldnames(mii)); diff --git a/projects/ML_based_MLSE/rop_evaluation.m b/projects/ML_based_MLSE/rop_evaluation.m new file mode 100644 index 0000000..eb77a3f --- /dev/null +++ b/projects/ML_based_MLSE/rop_evaluation.m @@ -0,0 +1,139 @@ +clear; clc; + +M = 4; +randkey = 1; +duob_mode = db_mode.no_db; + +mlse = 1; +dbtgt = 1; + +baudrates = 180e9:2e9:220e9; % outer loop +rops = linspace(-10,0,12); % inner sweep +FEC_thr = 3.8e-3; % BER target + +% --- allocate results +reqROP_FFE = nan(size(baudrates)); +reqROP_MLSE = nan(size(baudrates)); +reqROP_DBTGT = nan(size(baudrates)); +reqROP_ML_MLSE2 = nan(size(baudrates)); +reqROP_ML_MLSE3 = nan(size(baudrates)); + +%% ====================== OUTER LOOP ====================== +for b = 1:numel(baudrates) + baudrate = baudrates(b); + fprintf('\n=== %.0f GBd ===\n', baudrate/1e9); + + ber_ffe = nan(size(rops)); + ber_mlse = nan(size(rops)); + ber_dbtgt = nan(size(rops)); + ber_ml2 = nan(size(rops)); + ber_ml3 = nan(size(rops)); + + %% -------- inner ROP loop -------- + for i = 1:length(rops) + rop = rops(i); + + [Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1] = standard_link_model( ... + "M",M,"fsym",baudrate,"rop",rop,"laser_linewidth",1310, ... + "link_length_m",0,"random_key",1); + + + + %% FFE + MLSE + if mlse + pf_ncoeffs = 1; + ffe_order = [50, 0, 0]; + mu_ffe = [0.0001, 0.0008, 0.001]; + mu_dfe = 0.0004; + eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13, ... + "training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005, ... + "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).levels,'scale_mode',2, ... + 'trellis_exclusion',0,'trellis_state_mode',2,'debug',0, ... + 'DIR',pf_.coefficients); + + [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, ... + Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1, ... + "precode_mode", duob_mode,'showAnalysis', 0, "postFFE", [], ... + "eth_style_symbol_mapping", 0); + + ber_ffe(i) = ffe_results.metrics.BER; + ber_mlse(i) = mlse_results.metrics.BER; + end + + %% FFE + duobinary target MLSE + if dbtgt + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M, ... + "trellis_states",PAMmapper(M,0).levels,'scale_mode',2, ... + 'trellis_exclusion',0,'trellis_state_mode',3); + ffe_order = [50, 0, 0]; + mu_ffe = [0.0001, 0.0008, 0.001]; + mu_dfe = 0.0004; + eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13, ... + "training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005, ... + "DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0, ... + "plotfinal",0,"ideal_dfe",1); + + dbt_results = duobinary_target(eq_, mlse_db_, M, Rx_sig_2sps_v1, ... + Symbols_v1, Tx_bits_v1, "precode_mode", duob_mode, ... + 'showAnalysis', 0, "postFFE", []); + ber_dbtgt(i) = dbt_results.metrics.BER; + end + + %% ML-based MLSE (L=2) + mu_ml = 0.1; training_epochs = 100; + ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ... + "len_tr",2^16,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ... + "traceback_depth",128,"L",2,"delta",4,"adaptive_mu",0); + [y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1); + ref_bits = PAMmapper(M,0).demap(y_ref); + ml_bits = PAMmapper(M,0).demap(y_ml_mlse); + [~,~,ber_ml2(i)] = calc_ber(ml_bits.signal, ref_bits.signal, ... + "skip_front",10,"skip_end",10); + + %% ML-based MLSE (L=3) + ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ... + "len_tr",2^16,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ... + "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0); + [y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1); + ref_bits = PAMmapper(M,0).demap(y_ref); + ml_bits = PAMmapper(M,0).demap(y_ml_mlse); + [~,~,ber_ml3(i)] = calc_ber(ml_bits.signal, ref_bits.signal, ... + "skip_front",10,"skip_end",10); + end % ROP loop + + %% --- find required ROP (FEC crossing) + reqROP_FFE(b) = interp_fec_cross(rops, ber_ffe, FEC_thr); + reqROP_MLSE(b) = interp_fec_cross(rops, ber_mlse, FEC_thr); + reqROP_DBTGT(b) = interp_fec_cross(rops, ber_dbtgt, FEC_thr); + reqROP_ML_MLSE2(b) = interp_fec_cross(rops, ber_ml2, FEC_thr); + reqROP_ML_MLSE3(b) = interp_fec_cross(rops, ber_ml3, FEC_thr); + + % --- diagnostic + fprintf('Baud %.0f GBd: FFE %.1f, MLSE %.1f, DB %.1f, ML2 %.1f, ML3 %.1f\n', ... + baudrate/1e9, reqROP_FFE(b), reqROP_MLSE(b), reqROP_DBTGT(b), ... + reqROP_ML_MLSE2(b), reqROP_ML_MLSE3(b)); +end + +%% ====================== PLOT REQUIRED ROP ====================== +cols = cbrewer2('Set1',8); +colFFE = cols(1,:); +colMLSE = cols(2,:); +colDBTGT = cols(4,:); +colML_MLSE = cols(3,:); + +figure(); hold on +plot(baudrates/1e9, reqROP_FFE, '-o','Color',colFFE, 'DisplayName','FFE'); +plot(baudrates/1e9, reqROP_MLSE, '-s','Color',colMLSE, 'DisplayName','FFE+PF+MLSE'); +plot(baudrates/1e9, reqROP_DBTGT, '--^','Color',colDBTGT, 'DisplayName','DB tgt. MLSE'); +plot(baudrates/1e9, reqROP_ML_MLSE2, '-v','Color',colML_MLSE, 'DisplayName','ML-based MLSE (L=2)'); +plot(baudrates/1e9, reqROP_ML_MLSE3, '-d','Color',colML_MLSE*0.8,'DisplayName','ML-based MLSE (L=3)'); + +xlabel('Baud rate [GBd]'); +ylabel('Required ROP [dBm]'); +title('ROP required for FEC threshold'); +grid on; legend('Location','northwest'); +beautifyBERplot("logscale",0,"polyfit",1,"polyorder",4,"fitmethod",'polyfit'); diff --git a/projects/ML_based_MLSE/rrop_vs_length_evaluation.m b/projects/ML_based_MLSE/rrop_vs_length_evaluation.m new file mode 100644 index 0000000..9d2ea2b --- /dev/null +++ b/projects/ML_based_MLSE/rrop_vs_length_evaluation.m @@ -0,0 +1,140 @@ +clear; clc; + +M = 4; +randkey = 1; +duob_mode = db_mode.no_db; + +mlse = 1; +dbtgt = 1; + +link_lengths = 0:1:8; % [m] --- outer loop +rops = linspace(-10, 0, 12); % [dBm] --- inner sweep +FEC_thr = 3.8e-3; % BER target +baudrate = 200e9; + +% --- allocate results +reqROP_FFE = nan(size(link_lengths)); +reqROP_MLSE = nan(size(link_lengths)); +reqROP_DBTGT = nan(size(link_lengths)); +reqROP_ML_MLSE2 = nan(size(link_lengths)); +reqROP_ML_MLSE3 = nan(size(link_lengths)); + +%% ====================== OUTER LOOP ====================== +for L = 1:numel(link_lengths) + link_length_m = link_lengths(L); + fprintf('\n=== %.0f m fiber length ===\n', link_length_m); + + ber_ffe = nan(size(rops)); + ber_mlse = nan(size(rops)); + ber_dbtgt = nan(size(rops)); + ber_ml2 = nan(size(rops)); + ber_ml3 = nan(size(rops)); + + %% -------- inner ROP loop -------- + parfor i = 1:length(rops) + rop = rops(i); + + [Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1] = standard_link_model( ... + "M",M,"fsym",baudrate,"rop",rop,"laser_wavelength",1290, ... + "link_length_km",link_length_m,"random_key",1); + + Rx_sig_2sps_v1.spectrum("displayname",'Rx Sig','normalizeTo0dB',1); + + %% FFE + MLSE + if mlse + pf_ncoeffs = 1; + ffe_order = [50, 0, 0]; + mu_ffe = [0.0001, 0.0008, 0.001]; + mu_dfe = 0.0004; + eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13, ... + "training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005, ... + "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).levels,'scale_mode',2, ... + 'trellis_exclusion',0,'trellis_state_mode',2,'debug',0, ... + 'DIR',pf_.coefficients); + + [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, ... + Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1, ... + "precode_mode", duob_mode,'showAnalysis', 0, "postFFE", [], ... + "eth_style_symbol_mapping", 0); + + ber_ffe(i) = ffe_results.metrics.BER; + ber_mlse(i) = mlse_results.metrics.BER; + end + + %% FFE + duobinary target MLSE + if dbtgt + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M, ... + "trellis_states",PAMmapper(M,0).levels,'scale_mode',2, ... + 'trellis_exclusion',0,'trellis_state_mode',3); + ffe_order = [50, 0, 0]; + mu_ffe = [0.0001, 0.0008, 0.001]; + mu_dfe = 0.0004; + eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13, ... + "training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005, ... + "DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0, ... + "plotfinal",0,"ideal_dfe",1); + + dbt_results = duobinary_target(eq_, mlse_db_, M, Rx_sig_2sps_v1, ... + Symbols_v1, Tx_bits_v1, "precode_mode", duob_mode, ... + 'showAnalysis', 0, "postFFE", []); + ber_dbtgt(i) = dbt_results.metrics.BER; + end + + %% ML-based MLSE (L=2) + mu_ml = 0.1; training_epochs = 100; + ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ... + "len_tr",2^16,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ... + "traceback_depth",128,"L",2,"delta",4,"adaptive_mu",0); + [y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1); + ref_bits = PAMmapper(M,0).demap(y_ref); + ml_bits = PAMmapper(M,0).demap(y_ml_mlse); + [~,~,ber_ml2(i)] = calc_ber(ml_bits.signal, ref_bits.signal, ... + "skip_front",10,"skip_end",10); + + %% ML-based MLSE (L=3) + ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ... + "len_tr",2^16,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ... + "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0); + [y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1); + ref_bits = PAMmapper(M,0).demap(y_ref); + ml_bits = PAMmapper(M,0).demap(y_ml_mlse); + [~,~,ber_ml3(i)] = calc_ber(ml_bits.signal, ref_bits.signal, ... + "skip_front",10,"skip_end",10); + end % ROP loop + + %% --- find required ROP (FEC crossing) + reqROP_FFE(L) = interp_fec_cross(rops, ber_ffe, FEC_thr); + reqROP_MLSE(L) = interp_fec_cross(rops, ber_mlse, FEC_thr); + reqROP_DBTGT(L) = interp_fec_cross(rops, ber_dbtgt, FEC_thr); + reqROP_ML_MLSE2(L) = interp_fec_cross(rops, ber_ml2, FEC_thr); + reqROP_ML_MLSE3(L) = interp_fec_cross(rops, ber_ml3, FEC_thr); + + fprintf('Length %.0f m: FFE %.1f, MLSE %.1f, DB %.1f, ML2 %.1f, ML3 %.1f\n', ... + link_length_m, reqROP_FFE(L), reqROP_MLSE(L), reqROP_DBTGT(L), ... + reqROP_ML_MLSE2(L), reqROP_ML_MLSE3(L)); +end + +%% ====================== PLOT REQUIRED ROP ====================== +cols = cbrewer2('Set1',8); +colFFE = cols(1,:); +colMLSE = cols(2,:); +colDBTGT = cols(4,:); +colML_MLSE = cols(3,:); + +figure(); hold on +plot(link_lengths, reqROP_FFE, '-o','Color',colFFE, 'DisplayName','FFE'); +plot(link_lengths, reqROP_MLSE, '-s','Color',colMLSE, 'DisplayName','FFE+PF+MLSE'); +plot(link_lengths, reqROP_DBTGT, '--^','Color',colDBTGT, 'DisplayName','DB tgt. MLSE'); +plot(link_lengths, reqROP_ML_MLSE2, '-v','Color',colML_MLSE, 'DisplayName','ML-based MLSE (L=2)'); +plot(link_lengths, reqROP_ML_MLSE3, '-d','Color',colML_MLSE*0.8,'DisplayName','ML-based MLSE (L=3)'); + +xlabel('Link length [km]'); +ylabel('Required ROP [dBm]'); +title(sprintf('Required ROP the reach FEC threshold (3.8e-3); %.0f GBd PAM-%d', baudrate.*1e-9, M)); +legend('Location','northwest'); +grid on; +beautifyBERplot("logscale",0,"polyfit",1,"polyorder",3,"fitmethod",'smoothingspline'); diff --git a/projects/ML_based_MLSE/standard_link_model.m b/projects/ML_based_MLSE/standard_link_model.m new file mode 100644 index 0000000..004c249 --- /dev/null +++ b/projects/ML_based_MLSE/standard_link_model.m @@ -0,0 +1,99 @@ +function [Rx_sig_2sps,Symbols,Tx_bits] = standard_link_model(options) + + % STANDARD_LINK_MODEL Basic IM/DD link simulation + % Rx_sig_2sps = standard_link_model(...optional args...) + % + % All arguments are optional and default to standard parameters + % if not provided. + + arguments + + % --- Transmitter settings --- + options.M (1,1) double = 4 + options.apply_pulsef (1,1) logical = true + options.fdac (1,1) double = 256e9 + options.fadc (1,1) double = 256e9 + options.random_key (1,1) double = 2 + options.rcalpha (1,1) double = 0.05 + options.kover (1,1) double = 8 + options.vbias_rel (1,1) double = 0.5 + options.u_pi (1,1) double = 3.2 + options.laser_wavelength (1,1) double = 1310 + options.laser_linewidth (1,1) double = 1e6 + + % --- Channel parameters --- + options.link_length_km (1,1) double = 0 + options.rop (1,:) double = -5 + options.fsym (1,:) double = (212:16:256)*1e9 + options.doub_mode (1,1) db_mode = db_mode.no_db + + % --- Debug --- + options.debug (1,1) logical = false + + end + + % --- Pulse former --- + Pform = Pulseformer("fsym",options.fsym,"fdac",4*options.fsym, ... + "pulse","rc","pulselength",16,"alpha",options.rcalpha); + + % --- Transmitter source --- + [Digi_sig,Symbols,Tx_bits] = PAMsource( ... + "fsym",options.fsym,"M",options.M,"order",18,"useprbs",0, ... + "fs_out",options.fdac,"applyclipping",0,"clipfactor",1.5, ... + "applypulseform",options.apply_pulsef,"pulseformer",Pform, ... + "randkey",options.random_key,"db_precode",0,"db_encode",0, ... + "mrds_code",0,"mrds_blocklength",512, ... + "duobinary_mode",options.doub_mode).process(); + + % --- AWG driver --- + El_sig = M8199B("kover",options.kover).process(Digi_sig); + El_sig = El_sig.normalize("mode","oneone"); + + % --- E/O Modulation --- + vbias = -options.vbias_rel*options.u_pi; + Opt_sig = EML("mode",eml_mode.im_cosinus,"power",3, ... + "fsimu",El_sig.fs,"lambda",options.laser_wavelength, ... + "bias",vbias,"u_pi",options.u_pi,"linewidth",options.laser_linewidth, ... + "randomkey",options.random_key+1).process(El_sig); + + % --- Fiber --- + Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",options.link_length_km, ... + "alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); + + % --- Amplifier (ROP set) --- + Opt_sig = Amplifier("amp_mode","ideal_no_noise", ... + "gain_mode","output_power","amplification_db",options.rop).process(Opt_sig); + + % --- Photodiode --- + PD_sig = Photodiode("fsimu",options.fdac*options.kover,"dark_current",2e-8, ... + "responsivity",1,"temperature",20,"nep",1.8e-11, ... + "randomkey",options.random_key).process(Opt_sig); + + % --- Electrical LPF (receiver frontend) --- + rx_bwl = 70e9; + PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl, ... + "fs",options.fdac*options.kover,"filterType",filtertypes.butterworth, ... + "active",true).process(PD_sig); + + % --- Scope low-pass and sampling --- + Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",options.fadc, ... + "filterType",filtertypes.butterworth,"active",true); + + Scpe_sig = Scope("fsimu",options.fdac*options.kover,"fadc",options.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',1,'H_lpf',Lp_scpe).process(PD_sig); + + % --- Downsample to 2 sps --- + Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*options.fsym); + [~,Scpe_cell,~,found_sync] = Scpe_sig_2sps.tsynch( ... + "reference",Symbols,"fs_ref",options.fsym,"debug_plots",0); + + try + Rx_sig_2sps = Scpe_cell{1}.normalize("mode","rms"); + catch + Rx_sig_2sps = Scpe_sig_2sps.normalize("mode","rms"); + end + +end diff --git a/projects/ML_based_MLSE/theoretic_channel_evaluation.m b/projects/ML_based_MLSE/theoretic_channel_evaluation.m new file mode 100644 index 0000000..3eb6f09 --- /dev/null +++ b/projects/ML_based_MLSE/theoretic_channel_evaluation.m @@ -0,0 +1,157 @@ + +M = 4; +order = 18; +randkey = 1; + +bitpattern = []; +s = RandStream('twister','Seed',randkey); +for i = 1:log2(M) + N = 2^(order-1); %length of prbs + bitpattern(:,i) = randi(s,[0 1], N, 1); +end + +if M == 6 + bitpattern = reshape(bitpattern',[],1); + bitpattern = bitpattern(1:end-mod(length(bitpattern),5)); +end + +Bits = Informationsignal(bitpattern); + +Symbols = PAMmapper(M,0).map(Bits); +Symbols.fs = 200e9; + +Bits_ = PAMmapper(M, 0, "eth_style", 0).demap(Symbols); + +% --- Channel: minimal ISI response + AWGN --- +h = [0.3 0.9 0.3,0.1]; % impulse response (normalized later if desired) +h = h / norm(h); % optional normalization for unit energy + +symbols_filt = Symbols.filter(h,1); + + +%% SHOW FIG 3 in Paper: "ML Base Pre-Eq" + +SNR_dB = [20:1:25]; +SNR_db = linspace(12,25,12); + +ber_ffe = zeros(size(SNR_dB)); +ber_mlse_l5 = zeros(size(SNR_dB)); +ber_nwf_mlse_l2 = zeros(size(SNR_dB)); +ber_ml_mlse_l2 = zeros(size(SNR_dB)); +ber_ml_mlse_l3 = zeros(size(SNR_dB)); +ber_ml_mlse_l4 = zeros(size(SNR_dB)); + +epochs_training = 100; + +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)); + + % ML-base MLSE L=2 + adaptive_mu = 0; + mu_lms = 0.15; + ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... + "traceback_depth",128,"L",2,"delta",4,"adaptive_mu",adaptive_mu); + [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols); + ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + [~, errors, ber_ml_mlse_l2(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l2(i)); + + % ML-base MLSE L=3 + mu_lms = 0.15; + ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^16,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... + "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",adaptive_mu); + [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols); + ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + [~, errors, ber_ml_mlse_l3(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l3(i)); + + % % ML-base MLSE L=5 + % mu_lms = 0.15; + % ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,... + % "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... + % "traceback_depth",128,"L",5,"delta",4); + % [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols); + % ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + % [~, errors, ber_ml_mlse_l5(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + % fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l5(i)); + + +end + +%% +figure(); hold on; + +% --- define scheme colors (consistent palette) +cols = cbrewer2('SET1',8); +colFFE = cols(1,:); % blue +colMLSE = cols(2,:); % orange +colML_MLSE = cols(3,:); % green +colNWF_MLSE = cols(4,:); % purple + +% --- local simulation results +plot(SNR_dB, ber_ffe, '-o', 'Color', colFFE, 'DisplayName','FFE (N=16)'); +if M==2, plot(FFE_wpd.X, FFE_wpd.Y, ':', 'LineWidth',1.5, 'Color', colFFE, 'DisplayName','Paper FFE'); end + + +plot(SNR_dB, ber_mlse_l5, '-s', 'Color', colMLSE, 'DisplayName','MLSE (L=5)'); +if M==2, plot(MLSE_wpd.X, MLSE_wpd.Y, ':', 'LineWidth',1.5, 'Color', colMLSE, 'DisplayName','Paper MLSE L=5'); end + +plot(SNR_dB, ber_nwf_mlse_l2,'--^','Color', colNWF_MLSE, 'DisplayName','FFE+PF+MLSE (L=2)'); +plot(SNR_dB, ber_ml_mlse_l2, '-v', 'Color', colML_MLSE, 'DisplayName','ML-based MLSE (L=2)'); +if M==2, plot(ML_MLSE_L_2_wpd.X,ML_MLSE_L_2_wpd.Y,':', 'LineWidth',1.5, 'Color', colML_MLSE, 'DisplayName','Paper ML-based MLSE L=2'); end + + +plot(SNR_dB, ber_ml_mlse_l3, '-d', 'Color', colML_MLSE, 'DisplayName','ML-based MLSE (L=3)'); + +if M==2, plot(SNR_dB, ber_ml_mlse_l5, '-p', 'Color', colML_MLSE, 'DisplayName','ML-based MLSE (L=5)'); end +if M==2, plot(ML_MLSE_L_5_wpd.X,ML_MLSE_L_5_wpd.Y,':', 'LineWidth',1.5, 'Color', colML_MLSE, 'DisplayName','Paper ML-based MLSE L=5'); end +% --- imported WebPlotDigitizer data (dotted) + +yline(3.8e-3,'HandleVisibility','off'); +yline(2.2e-4,'HandleVisibility','off'); + +% --- formatting +beautifyBERplot; +xlim([SNR_dB(1), SNR_dB(end)]); +ylim([1e-5 0.1]); +xlabel('Input SNR [dB]'); +ylabel('Bit Error Rate (BER)'); +title('PAM-4; M=4; AWGN Channel'); +legend('Location','southwest'); +grid on; + diff --git a/projects/ML_based_MLSE/wpd_datasets.csv b/projects/ML_based_MLSE/wpd_datasets.csv new file mode 100644 index 0000000..c2059d7 --- /dev/null +++ b/projects/ML_based_MLSE/wpd_datasets.csv @@ -0,0 +1,9 @@ +FFE,,MLSE,,ML MLSE L=2,,ML MLSE L=5, +X,Y,X,Y,X,Y,X,Y +7.988476019722099,0.038632829886662855,7.9828552218736,0.02056096426419196,7.988825638727029,0.026145160025945406,7.982882115643211,0.01995262314968881 +8.984755714926044,0.02462092401494627,8.991855670103094,0.008868008219069292,8.991519497982967,0.012908315306800594,8.998027790228598,0.009002182769536628 +9.993487225459436,0.014339094903163154,9.988632900044824,0.0032424222072799827,9.994320932317347,0.005651632488900345,9.994751232631108,0.0034952501326754757 +10.989914836396235,0.007746944324122318,10.991730165844913,0.001020224273461357,10.997256835499776,0.002129417748571358,10.991689825190498,0.0010672369906432938 +12.005060510981624,0.0034952501326754757,12.001483639623487,0.00018978455660928717,11.994316450022412,0.0005679993334792185,12.007359928283282,0.0002680778116002006 +12.983254146122816,0.0013169376605490738,13.011492604213357,0.000026540740973404924,12.997722994173017,0.00012652429120320801,12.992384580905423,0.000049125201846734525 +13.99255042581802,0.0004081967712510506,14.00969520394442,0.000001975386920666194,13.995172568354999,0.00002183385565256239,14.015275661138503,0.0000038826695948713645 diff --git a/projects/Nonlinear_MLSE/simulation_nonlin_dsp.m b/projects/Nonlinear_MLSE/simulation_nonlin_dsp.m new file mode 100644 index 0000000..1896dd4 --- /dev/null +++ b/projects/Nonlinear_MLSE/simulation_nonlin_dsp.m @@ -0,0 +1,229 @@ +%%% Run parameters +% TX +M = 4; +m = floor(log2(M)*10)/10; +fsym = 224e9; + +apply_pulsef = 1; +fdac = 256e9; +fadc = 256e9; +random_key = 2; + +rcalpha = 0.05; +kover = 8; +vbias_rel = 0.5; +u_pi = 3.2; +vbias = -vbias_rel*u_pi; +laser_wavelength = 1310; +laser_linewidth = 1e6; + + +% Channel +link_length = 0; + +vnle_order1 = 50; +vnle_order2 = 0; +vnle_order3 = 0; + +vnle_order=[vnle_order1,vnle_order2,vnle_order3]; +dfe_order = [0 0 0]; + +alpha = 0; + +len_tr = 4096*2; + +mu_ffe1 = 0.0001; +mu_ffe2 = 0.0008; +mu_ffe3 = 0.001; +mu_dc = 0.005; +% mu_dc = 0; + +mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; +mu_dfe = 0.0004; + +dfe_ = sum(dfe_order)>0; + +doub_mode = db_mode.no_db; +cols = linspecer(6); +rop = [-8]; +bwl = [0.5:0.1:1.5]; +fsym = [208:16:256].*1e9; +nonlin_mod = [0.5:0.01:0.75]; +fsym = ones(size(nonlin_mod)).*fsym(1); + +ffe_results = {}; +mlse_results_lin= {}; +vnle_results= {}; +mlse_results_nonlin= {}; +mlse_results_nonlin_states= {}; + +g_eye = GifWriter('Name','eye','Parallel',true); +g_mod = GifWriter('Name','modulator','Parallel',true); + +for r = 1:length(nonlin_mod) + + Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha); + + db_precode = 0; + db_encode = 0; + duob_mode = db_mode.no_db; + apply_pulsef = 1; + + [Digi_sig,Symbols,Tx_bits] = PAMsource(... + "fsym",fsym(r),"M",M,"order",19,"useprbs",0,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.5,... + "applypulseform",apply_pulsef,"pulseformer",Pform,... + "randkey",random_key,... + "db_precode",db_precode,"db_encode",db_encode,... + "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); + + % El_sig = AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig); + El_sig = M8199B("kover",kover).process(Digi_sig); + % AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig); + + %%%%% Low-pass el. components %%%%%% + % tx_bwl = 100e9; + % El_sig = Filter('filtdegree',3,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); + + %%%%% Electrical Driver Amplifier %%%%%% + El_sig = El_sig.normalize("mode","oneone"); + % El_sig = El_sig.setPower(1,"dBm"); + % figure;histogram(El_sig.signal); + + %%%%% MODULATE E/O CONVERSION %%%%% + u_pi = 3.2; + vbias = -u_pi*nonlin_mod(r); + [Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1).process(El_sig); + + if 1 + figure(15); + hold on + scatter(El_sig.signal(1:100000)+vbias,(abs(Opt_sig.signal(1:100000)).^2)*1e3,0.1,'.','DisplayName','Modulator TF') + xlabel('Input in V') + ylabel('abs(Eopt)2 in mW','Interpreter','latex') + ylim([0 2]); + xlim([-3.2 0]); + g_mod.addFrame(15, r); + + Opt_sig.eye(fsym(r), M, "fignum", 103837); + g_eye.addFrame(103837, r); + end + + %%%%%% Fiber %%%%%% + Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); + + %%%%%% ROP %%%%%% + Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); + + %%%%%% PD Square Law %%%%%% + PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",random_key).process(Opt_sig); + + %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% + rx_bwl = 70e9; + PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig); + + % %%%%%% Low-pass Scope %%%%%% + Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + + %%%%%% Scope %%%%%% + 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',1,'H_lpf',Lp_scpe).process(PD_sig); + + Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym(r)); + % Scpe_sig_resampled.signal = Scpe_sig_resampled.signal(1:2*length(Symbols)); + + [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0); + Rx_sig = Scpe_cell{1}; + Rx_sig = Rx_sig.normalize("mode","rms"); + + if 1 + + %% FFE + % 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,M,Rx_sig,Symbols,Tx_bits,... + % "precode_mode",duob_mode,... + % 'showAnalysis',0,... + % "postFFE",[],... + % "eth_style_symbol_mapping",0); + % + % ffe_results.metrics.print; + % ffe_results.config.equalizer_structure = "ffe"; + % + + %% MLSE linear + + pf_ncoeffs = 1; + ffe_order = [50, 0, 0]; + eq_ = 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",1); + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',2,'debug',1); + + [ffe_results{r}, mlse_results_lin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode,... + 'showAnalysis', 0, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + mlse_results_lin{r}.metrics.print; + ffe_results{r}.metrics.print; + + %% MLSE nonlinear pre + + pf_ncoeffs = 1; + ffe_order = [50, 1, 0]; + eq_ = 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",1); + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',2); + + [vnle_results{r}, mlse_results_nonlin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode,... + 'showAnalysis', 0, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + mlse_results_nonlin{r}.metrics.print; + vnle_results{r}.metrics.print; + + %% nonlinear states MLSE linear pre + + pf_ncoeffs = 1; + ffe_order = [50, 0, 0]; + eq_ = 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",1); + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',3); + + [~, mlse_results_nonlin_states{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode,... + 'showAnalysis', 0, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + mlse_results_nonlin_states{r}.metrics.print; + + end +end + +g_mod.compile(15); +g_eye.compile(103837); + +%% + +figure();hold on; +plot(nonlin_mod,cellfun(@(x) x.metrics.BER, ffe_results),'DisplayName','FFE') +plot(nonlin_mod,cellfun(@(x) x.metrics.BER, vnle_results),'DisplayName','VNLE') +plot(nonlin_mod,cellfun(@(x) x.metrics.BER, mlse_results_lin),'DisplayName','FFE+MLSE') +plot(nonlin_mod,cellfun(@(x) x.metrics.BER, mlse_results_nonlin_states),'DisplayName','FFE+nonlin. states MLSE') +plot(nonlin_mod,cellfun(@(x) x.metrics.BER, mlse_results_nonlin),'DisplayName','VNLE+MLSE') +xlabel('Nonlinear Driving'); +ylabel('BER') +set(gca,'YScale','log'); +legend; +ylim([1e-4 1e-1]); +beautifyBERplot; + + diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m new file mode 100644 index 0000000..d13c97f --- /dev/null +++ b/projects/WDM/WDM_auswertung.m @@ -0,0 +1,252 @@ + + +try + rop = res.settings.rop; % 12 points + wavelengthplan = res.settings.wavelengthplan; +catch + wavelengthplan = [1295,1305,1315,1325]; + wavelengthplan = calcWavelengthPlan(16,400e9,1310); + rop = -8.25:0.75:0; +end + +N = length(wavelengthplan); +figure(); hold on; +cols = cbrewer2('set2',N); % one color per wavelength (Ch) + +fec = 2.2e-4; +fec = 3.8e-3; +Sffe = cell(1,N); +Svnle = cell(1,N); +Smlse = cell(1,N); +Sdbt = cell(1,N); + +% Choose your quantile band. For your old style, use 0.04/0.99: +qLow = 0.0; % lower quantile (e.g., 0.04 for old script) +qHigh = 1; % upper quantile (e.g., 0.99 for old script) +cols = linspecer(N); % one color per wavelength (Ch) +cols = cbrewer2('set1',N); + +for l = 1:N + % Slice 12x50 cell arrays + ffe_cells = reshape(squeeze(res.ffe(l,:,:)),length(rop),[]); + vnle_cells = reshape(squeeze(res.vnle(l,:,:)),length(rop),[]); + mlse_cells = reshape(squeeze(res.mlse(l,:,:)),length(rop),[]); + dbt_cells = reshape(squeeze(res.dbt(l,:,:)),length(rop),[]); + + [Sffe{l}, noX_ffe] = fecCrossings(rop, ffe_cells, fec); + + [Svnle{l}, noX_ffe] = fecCrossings(rop, vnle_cells, fec); + + [Smlse{l}, noX_ffe] = fecCrossings(rop, mlse_cells, fec); + + [Sdbt{l}, noX_ffe] = fecCrossings(rop, dbt_cells, fec); + + % Extract BER matrices using only complete realizations (12/12 ROP filled) + ffe_mat = extractCompleteBER(ffe_cells); % 12 x K_ffe + vnle_mat = extractCompleteBER(vnle_cells); % 12 x K_vnle + mlse_mat = extractCompleteBER(mlse_cells); % 12 x K_mlse + mlse_alpha_mat = extractCompleteAlphas(mlse_cells); % 12 x K_mlse + dbt_mat = extractCompleteBER(dbt_cells); % 12 x K_dbt + + showLegend = 1; % one legend entry per technique + + % Plot shaded band + mean line with boundedline + % plotBandMeanBL(rop, ffe_mat, cols(l,:), sprintf('FFE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--s', showLegend); + % scatter(Sffe,fec.*ones(size(Sffe)),20,'v','MarkerFaceColor','black'); + + plotBandMeanBL(rop, vnle_mat, cols(l,:), sprintf('VNLE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--x', showLegend); + + % plotBandMeanBL(rop, mlse_mat, cols(l,:), sprintf('VNLE+PF+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '-o', showLegend); + + % plotBandMeanBL(rop, dbt_mat, cols(l,:), sprintf('DBt.+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--v', showLegend); + + set(gca,'XScale','linear','YScale','log','TickLabelInterpreter','latex','FontSize',11); + yline([3.8e-3, 2.2e-4], 'HandleVisibility','off','LineWidth',1.5); + +end + +ylabel('BER'); +xlabel('ROP'); +title('BER vs. ROP'); +xlim([min(rop) max(rop)]); +ylim([1e-5 0.3]); +grid on; +legend show; + + +S_cell = Sdbt; +S_cell =Smlse; +S_cell = {Svnle,Smlse,Sdbt}; +S_cell = {Svnle}; +figure(5); hold on; +for i = 1:length(S_cell) + % Pad to rectangular matrix: rows = realizations, cols = wavelengths + Kmax = max(cellfun(@numel, S_cell{i})); + S_mat = NaN(Kmax, N); + for l = 1:N + k = numel(S_cell{i}{l}); + if k > 0 + S_mat(1:k, l) = S_cell{i}{l}; + end + end + + % --- Violin plot over wavelengths (columns) --- + + cols=linspecer(3); + catLabels = arrayfun(@(nm) sprintf('%d nm', nm), wavelengthplan, 'UniformOutput', false); + vs = violinplot(S_mat, catLabels, ... + 'ViolinColor', cols(i,:), ... + 'ViolinAlpha', 0.10, ... + 'MarkerSize', 20, ... + 'ShowMedian', true, ... + 'EdgeColor', cols(i,:), ... + 'ShowWhiskers', false, ... + 'ShowData', true, ... + 'ShowBox', false, ... + 'Bandwidth', 0.05); + + ylim([floor(min(S_mat,[],'all')), ceil(max(S_mat,[],'all'))]) + ylim([-8 0]); + ylabel('ROP at FEC crossing'); + title(sprintf('RROP to cross BER %.2e', fec)); + grid on; box on; + +end + + + + + + + + + + +%% ================= helper ================= +function plotBandMeanBL(x, Y, color, techLabel, qLow, qHigh, lineSpec, showLegend) + % Y: (nPoints x nRealizations) + % Remove realizations that are entirely zero (like removeZeros behavior) + badCols = all(Y == 0, 1); + Y(:, badCols) = []; + + Y(Y==0) = 1e-8; + % Stats across realizations + mu = mean(Y, 2, 'omitnan'); % mean line + lo = quantile(Y, qLow, 2); % lower bound + hi = quantile(Y, qHigh, 2); % upper bound + + % Convert to asymmetric distances required by boundedline: + % b(:,1) = distance to lower side; b(:,2) = distance to upper side + b = [mu - lo, hi - mu]; + + % Call boundedline with alpha shading + [hl, hp] = boundedline(x(:), mu(:), b, lineSpec, 'alpha', ... + 'transparency', 0.18); + % Color styling + set(hl, 'Color', color, 'LineWidth', 1.4, 'MarkerSize', 4); + set(hp, 'FaceColor', color, 'HandleVisibility','off'); % patch hidden in legend + + % Single legend entry per technique (use first wavelength only) + if showLegend + set(hl, 'DisplayName', techLabel); + else + set(hl, 'HandleVisibility','off'); + end + + % Optional: outline the bounds if outlinebounds is available + if exist('outlinebounds','file') == 2 + ho = outlinebounds(hl, hp); + set(ho, 'linestyle', ':', 'color', color, 'linewidth', 1, ... + 'HandleVisibility','off'); + end +end + +function [S, noCrossingMask, Y_keep] = fecCrossings(rop, cells12xR, fec) +% cells12xR: 12xR cell array (one wavelength + scheme slice) +% each cell must be a struct with .metrics.BER +% rop: 12x1 numeric vector of ROP points +% fec: scalar FEC threshold (e.g., 3.8e-3) +% +% Outputs: +% S 1xK vector of crossing ROP per kept realization (NaN if none) +% noCrossingMask 1xK logical mask: true if no crossing for that realization +% Y_keep 12xK numeric BER matrix used for the crossing detection + + % 1) keep only complete realization columns + Y = extractCompleteBER(cells12xR); % -> 12 x K + if isempty(Y) + S = []; + noCrossingMask = []; + Y_keep = Y; + return; + end + + % 2) optionally drop realizations with mean BER > 0.1 + ok = mean(Y,1,'omitnan') <= 0.1; + Y = Y(:, ok); + if isempty(Y) + S = []; + noCrossingMask = []; + Y_keep = Y; + return; + end + + % 3) find crossings per realization + nR = size(Y,2); + S = nan(1,nR); + noCrossingMask = true(1,nR); + + rop = rop(:); % ensure column + for j = 1:nR + y = Y(:,j); + + % sign change from >fec to <=fec (first time it drops below FEC) + above = (y > fec); + idx = find(above(1:end-1) & ~above(2:end), 1, 'first'); + + if ~isempty(idx) + % linear interpolation between (x1,y1) and (x2,y2) + x1 = rop(idx); y1 = y(idx); + x2 = rop(idx+1); y2 = y(idx+1); + + if isfinite(y1) && isfinite(y2) && y2 ~= y1 + t = (fec - y1) / (y2 - y1); + S(j) = x1 + t*(x2 - x1); + noCrossingMask(j) = false; + end + end + end + + Y_keep = Y; +end + + + +function Y = extractCompleteBER(cellSlice) +% cellSlice: 12xR cell array; each cell should be a struct with .metrics.BER +% Keep only those realization columns where ALL 12 ROP entries are valid. + if isempty(cellSlice), Y = []; return; end + nR = size(cellSlice,2); + keep = false(1,nR); + for r = 1:nR + col = cellSlice(:,r); + keep(r) = all(cellfun(@(c) ~isempty(c) , col)); + end + if ~any(keep), Y = []; return; end + Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true); +end + +function Y = extractCompleteAlphas(cellSlice) +% cellSlice: 12xR cell array; each cell should be a struct with .metrics.BER +% Keep only those realization columns where ALL 12 ROP entries are valid. + if isempty(cellSlice), Y = []; return; end + nR = size(cellSlice,2); + keep = false(1,nR); + for r = 1:nR + col = cellSlice(:,r); + keep(r) = all(cellfun(@(c) ~isempty(c) , col)); + end + if ~any(keep), Y = []; return; end + Y = cellfun(@(c) c.metrics.Alpha, cellSlice(:,keep), 'UniformOutput', true); +end + diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index 921c705..77247d0 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -15,9 +15,9 @@ m = floor(log2(M)*10)/10; fsym = 224e9; fdac = 2*fsym; fadc = 2*fsym; -random_key = 2; +s.random_key = 100; -% Laser / Modulator +% Laser / s.Modulator vbias_rel = 0.5; u_pi = 3.2; vbias = -vbias_rel*u_pi; @@ -47,8 +47,8 @@ apply_pulsef = 0; rcalpha = 0.05; Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); -N = numel(wavelengthplan); -f_plan = physconst('lightspeed')./(wavelengthplan.*1e-9); +N = numel(s.wavelengthplan); +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; @@ -57,38 +57,38 @@ upsample_required = f_nyq./(fdac*kover/2); upsample_pow = 2^nextpow2(upsample_required); upsample_ceil = ceil(upsample_required); -f_opt = fdac*kover*upsample_pow; -f_opt_nyq = f_opt/2; +s.f_opt = fdac*kover*upsample_pow; +s.f_opt_nyq = s.f_opt/2; signal_cell = {}; Symbols = {}; Tx_bits = {}; -rop = -8.25:0.75:0; +s.rop = -6:0.75:-0.75; -output_ffe = cell(length(wavelengthplan),length(rop),num_realiz); -output_vnle = cell(length(wavelengthplan),length(rop),num_realiz); -output_mlse = cell(length(wavelengthplan),length(rop),num_realiz); -output_dbt = cell(length(wavelengthplan),length(rop),num_realiz); +output_ffe = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); +output_vnle = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); +output_mlse = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); +output_dbt = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); -for realiz = 1:num_realiz +for realiz = 1:s.num_realiz parfor l = 1:N [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(... - "fsym",fsym,"M",M,"order",18,"useprbs",0,... + "fsym",fsym,"M",s.M,"order",18,"useprbs",0,... "fs_out",fdac,... "applyclipping",0,"clipfactor",1.5,... "applypulseform",apply_pulsef,"pulseformer",Pform,... - "randkey",random_key+l+realiz,... + "randkey",s.random_key+l+realiz,... "db_precode",db_precode,"db_encode",db_encode,... "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); % Digi_sig.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); Lp_awg = Filter('filtdegree',3,"f_cutoff",100e9,"fs",fdac*kover,"filterType",filtertypes.gaussian,"active",true); El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0,"H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig); - % El_sig = M8199B("kover",kover).process(Digi_sig); + % El_sig = s.M8199B("kover",kover).process(Digi_sig); % El_sig.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); %%%%% Electrical Driver Amplifier %%%%%% @@ -96,10 +96,10 @@ for realiz = 1:num_realiz % El_sig = El_sig.setPower(1,"dBm"); % figure;histogram(El_sig.signal); - %%%%% MODULATE E/O CONVERSION %%%%% - Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",wavelengthplan(l),"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+l+realiz).process(El_sig); + %%%%% s.MODULATE 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+realiz).process(El_sig); - signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",30).process(Eml_out); + signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",100).process(Eml_out); end Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover,... @@ -119,12 +119,12 @@ for realiz = 1:num_realiz zdw = 1310; D_local = 0; %if ~=0, simulation uses "segmented fiber with d+,d-) randomize_D = true; - Dvec = getDispersionVector(nSegments, D_local, zdw, randomize_D, random_key+realiz); - for s = 1:nSegments + Dvec = getDispersionVector(nSegments, D_local, zdw, randomize_D, s.random_key+realiz); + for seg = 1:nSegments Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(s),"Dpmd",pmd,"Ds",0.07,... "beat_len",10,"corr_len",100,"dz",1,"manakov",0,... - "gamma",gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01,... + "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); end @@ -133,12 +133,12 @@ for realiz = 1:num_realiz % Opt_sig_wdm_fib.move_it_spectrum("fignum",100212,"displayname",'bla'); - % Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig) + % Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",s.link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"s.gamma",0,"Dslope",0.07).process(Opt_sig) - parfor ri = 1:length(rop) + for ri = 1:length(s.rop) %%%%%% ROP %%%%%% - Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop(ri)+10*log10(N)).process(Opt_sig_wdm_fib); + Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.rop(ri)+10*log10(N)).process(Opt_sig_wdm_fib); Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",Opt_sig_wdm_rx.fs/upsample_pow,"fs_in",Opt_sig_wdm_rx.fs,"lambda_center",1310).process(Opt_sig_wdm_rx); @@ -147,9 +147,9 @@ for realiz = 1:num_realiz %%%%%% PD Square Law %%%%%% assert(fdac*kover==Opt_sig_wdm_demux{l}.fs,'Sampling Frequencies do not match! Check previous steps'); - PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",random_key+l+realiz).process(Opt_sig_wdm_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+realiz).process(Opt_sig_wdm_demux{l}); - PD_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1); + % PD_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1); %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% rx_bwl = 100e9; @@ -176,7 +176,7 @@ for realiz = 1:num_realiz % FFE 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,M,Rx_sig,Symbols{l},Tx_bits{l},... + ffe_results = ffe(eq_ffe,s.M,Rx_sig,Symbols{l},Tx_bits{l},... "precode_mode",duob_mode,... 'showAnalysis',0,... "postFFE",[],... @@ -194,12 +194,12 @@ for realiz = 1:num_realiz useviterbi = 0; if useviterbi - mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + mlse_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels); else - mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + mlse_ = MLSE("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels); end - [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig, Symbols{l},Tx_bits{l}, ... + [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, s.M, Rx_sig, Symbols{l},Tx_bits{l}, ... "precode_mode", duob_mode,... 'showAnalysis', 0, ... "postFFE", [],... @@ -209,18 +209,17 @@ for realiz = 1:num_realiz output_mlse{l,ri,realiz} = mlse_results; - % DB tgt. useviterbi = 0; if useviterbi - mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels); else - mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",s.M,"trellis_states",PAMmapper(s.M,0).levels); end ffe_order = [50, 5, 5]; eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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); - dbt_results = duobinary_target(eq_, mlse_db_, M, Rx_sig, Symbols{l},Tx_bits{l}, ... + dbt_results = duobinary_target(eq_, mlse_db_, s.M, Rx_sig, Symbols{l},Tx_bits{l}, ... "precode_mode", duob_mode, ... 'showAnalysis', 0,... "postFFE", []); @@ -232,6 +231,7 @@ for realiz = 1:num_realiz end res = struct(); + res.settings = s; res.ffe = output_ffe; res.vnle = output_vnle; res.mlse = output_mlse; @@ -240,38 +240,13 @@ for realiz = 1:num_realiz % Save results save(fullfile(output_root, fname), 'res', '-v7.3'); fprintf('Saved results to: %s\n', fullfile(output_root, fname)); + disp(datetime('now','TimeZone','local','Format','yyyyMs.Mdd_HHmmss')); + end - - -figure();hold on; -cols = linspecer(N); -for l = 1:N - % plot(rop,mean(squeeze(ber_vnle(l,:,:)),2,'omitnan'),'Marker','*','DisplayName',sprintf('Ch: %d',wavelengthplan(l))) - % plot(rop,cellfun(@(c) c.metrics.BER, output_ffe(l,:), 'UniformOutput', true),'Marker','*','DisplayName',sprintf('Ch: %d',wavelengthplan(l)),'Color',cols(l,:),'HandleVisibility','on','LineStyle',':'); - plot(rop,cellfun(@(c) c.metrics.BER, res.vnle(l,:), 'UniformOutput', true),'Marker','x','DisplayName',sprintf('Ch: %d',wavelengthplan(l)),'Color',cols(l,:),'HandleVisibility','on','LineStyle','--') - plot(rop,cellfun(@(c) c.metrics.BER, res.mlse(l,:), 'UniformOutput', true),'Marker','o','DisplayName',sprintf('Ch: %d',wavelengthplan(l)),'Color',cols(l,:),'HandleVisibility','on','LineStyle','-') -end -yline([3.8e-3,2.2e-4],'HandleVisibility','off'); -ylabel('BER'); -xlabel('ROP') -title('BER vs. ROP'); -set(gca, 'XScale', 'linear', ... - 'YScale', 'log', ... - 'TickLabelInterpreter', 'latex', ... - 'FontSize', 11); -xlim([min(rop) max(rop)]) -ylim([1e-5 0.3]) - -% --- save as PNG --- -outname = fullfile(output_root, 'BER_vs_ROP.png'); % saves to current folder -print(gcf, outname, '-dpng', '-r300'); % 300 dpi -fprintf('Saved figure to %s\n', outname); - - function dispersion_vector = getDispersionVector(N, D, ref_zdw, randomize_ZDW, randomkey) -% MATLAB version of the Python generator shown above. +% s.MATLAB version of the Python generator shown above. % Returns an N×1 vector (ps/(nm·km)). % % D is the nominal dispersion magnitude. For D>0 the link is segmented with diff --git a/projects/run_loop.m b/projects/run_loop.m index 4eedbe8..537a34e 100644 --- a/projects/run_loop.m +++ b/projects/run_loop.m @@ -13,41 +13,45 @@ end database_name = 'ecoc2025_loops.db'; -db = DBHandler("pathToDB", [databasePath, database_name],"type",'mysql'); - +db = DBHandler("pathToDB", [databasePath, database_name]); conf = db.tables.Configurations; +% conf = db.tables.Configurations; if ~exist('confPrev','var') - confPrev = db.tables.Configurations; + confPrev = conf; end awg_upload_required = 1; %still uploads every first round subimt_DSP = 1; -recordlen = 5000000; +recordlen = 1000000; sequence_order = 17; n_recording = 1; % Or any number you want +conf=rmfield(conf,"interference_attenuation"); +conf=rmfield(conf,"symbolrate"); +conf=rmfield(conf,"pam_level"); + % Define vector parameters directly in conf: conf.unique_elab_id = "20250408-842b0a3078172b48dd032795226fbe683190afc4"; conf.db_mode = db_mode.no_db; conf.pulsef_alpha = 0.2; conf.v_bias = 2.65; -conf.v_awg = 0.9; +conf.v_awg = 0.85; conf.precomp_amp = -64; %-64 conf.wavelength = 1310; conf.laser_power = 11; conf.fiber_length = 0; conf.pd_in_desired = 9; -conf.is_mpi = 1; +conf.is_mpi = true; conf.signal_attenuation = 0; -conf.interference_path_length = 0; %m -conf.interference_attenuation = 40;%[0:2:30,40]; -conf.pam_level = 4;%[4,6,8]; -conf.symbolrate = 56e9;%[56,72,96,112].*1e9; -conf.bitrate = conf.symbolrate .* floor(log2(6)*10)/10; +conf.interference_path_length = 20; %m +conf.interference_attenuation = 10;%[0:2:30,40]; +conf.pam_level = 4;%[2,4,6,8]; +conf.symbolrate = [112].*1e9; + conf.pam_source = []; if conf.is_mpi - current_folder = ['mpi_',num2str(conf.interference_path_length),'m_pam',num2str(conf.pam_level)]; + current_folder = ['test_mpi_opti_',num2str(conf.interference_path_length),'m_pam',num2str(conf.pam_level)]; else current_folder = 'testing'; end @@ -105,10 +109,23 @@ else end end - fprintf('Running measurement: %s\n', strjoin(paramDisplayCell, ', ')); + fprintf('Running measurement: %s ---> %d of %d done \n ', strjoin(paramDisplayCell, ', '),runIdx, totalRuns); + + if confRequest.pam_level == 2 + confRequest.symbolrate = 112e9; + elseif confRequest.pam_level == 4 + confRequest.symbolrate = 112e9; + elseif confRequest.pam_level == 6 + confRequest.symbolrate = 96e9; + elseif confRequest.pam_level == 8 + confRequest.symbolrate = 72e9; + end + confRequest.bitrate = confRequest.symbolrate * floor(log2(confRequest.pam_level) * 10) / 10; % measure_run("config",confRun,"parallel",parallel_dsp,"max_occurences",max_occurences); - measure_run_script; + + measure_run_script; + end end diff --git a/projects/standard_system/freqresp_test.m b/projects/standard_system/freqresp_test.m index d305da3..f0cab66 100644 --- a/projects/standard_system/freqresp_test.m +++ b/projects/standard_system/freqresp_test.m @@ -1,28 +1,19 @@ -measure = 0; +measure = 1; + +freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",70,"f_ref",256e9); +% +Digi_sig = freqresp.buildOFDM(); + +Digi_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]); + +Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.butterworth,"active",true).process(Digi_sig); + +Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.bessel_inp,"active",true).process(Digi_sig); -if measure - freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",70,"f_ref",256e9); - % - Digi_sig = freqresp.buildOFDM(); -else - [Digi_sig,Symbols,Bits] = PAMsource("fsym",fsym,"M",M,"order",18,"useprbs",0,... - "fs_out",M8199.fdac,"applyclipping",1,"clipfactor",1.7,"applypulseform",1,"pulseformer",Pform,"randkey",pn_key,"mrds_code",usemrds,"mrds_blocklength",512).process(); -end +freqresp.estimate(Digi_sig,"fileName",'','save',false); -Digi_sig.spectrum("fignum",1112,"displayname",['Signal']); +freqresp.plot() -maxamp = -1; -El_sig = freqresp.precomp(Digi_sig,"maxampdb",maxamp); - -El_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]); - -El_sig = Filter('filtdegree',2,"f_cutoff",60e9,"fs",256e9,"filterType",filtertypes.butterworth,"active",true).process(El_sig); - -if measure - freqresp.estimate(El_sig,"fileName",'','save',false); -end - - - -El_sig.spectrum("fignum",1112,"displayname",['after filter; maxamp:',num2str(maxamp)]); \ No newline at end of file +a = gca; +a.YTick = [-30,-20,-10,0];