From 727c3d93641ecf2d4c28ce56cd8ba28db4ab0e7c Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Fri, 25 Apr 2025 10:31:43 +0200 Subject: [PATCH 01/30] auswertungsfiles und algos for ECOC 2025 rush... --- Classes/00_signals/Signal.m | 2 +- Classes/04_DSP/Equalizer/FFE_DCremoval.m | 19 +- .../Equalizer/FFE_DCremoval_adaptive_mu.m | 272 ++++++++++++++++++ .../04_DSP/Equalizer/FFE_DCremoval_level.m | 240 ++++++++++++++++ Classes/DataBaseHandler/DBHandler.m | 2 +- Functions/EQ_structures/vnle.m | 269 ++++++++++------- .../EQ_structures/vnle_postfilter_mlse.m | 7 +- Functions/EQ_visuals/showLevelScatter.m | 10 +- Functions/Metrics/count_error_bursts.m | 29 ++ projects/ECOC_2025/dsp_loop_id.m | 63 ++++ projects/ECOC_2025/dsp_run_id.m | 63 ++-- projects/ECOC_2025/dsp_standalone.m | 92 +++++- .../ECOC_2025/dsp_test/hyperparam_tuning.m | 58 ++++ projects/ECOC_2025/load_signal_standalone.m | 66 +++-- .../plots_from_database/plot_mpi_trial.m | 173 +++++++---- projects/ECOC_2025/submit_dsp.m | 7 +- .../auswertung MPI/dsp_mpi_database.m | 11 +- .../auswertung MPI/dsp_run_id.m | 2 +- 18 files changed, 1152 insertions(+), 233 deletions(-) create mode 100644 Classes/04_DSP/Equalizer/FFE_DCremoval_adaptive_mu.m create mode 100644 Classes/04_DSP/Equalizer/FFE_DCremoval_level.m create mode 100644 Functions/Metrics/count_error_bursts.m create mode 100644 projects/ECOC_2025/dsp_loop_id.m create mode 100644 projects/ECOC_2025/dsp_test/hyperparam_tuning.m diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index ff6e05b..dd4cf9e 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -141,7 +141,7 @@ classdef Signal arguments obj - options.fignum + options.fignum = [] options.displayname = []; options.timeframe = 0; options.clear = 0; diff --git a/Classes/04_DSP/Equalizer/FFE_DCremoval.m b/Classes/04_DSP/Equalizer/FFE_DCremoval.m index dafe65c..06d50c6 100644 --- a/Classes/04_DSP/Equalizer/FFE_DCremoval.m +++ b/Classes/04_DSP/Equalizer/FFE_DCremoval.m @@ -57,6 +57,8 @@ classdef FFE_DCremoval < handle obj.e = zeros(obj.order,1); obj.error = 0; + obj.dc_buffer_len = floor(obj.dc_buffer_len); + end function [X,Noi] = process(obj, X, D) @@ -155,16 +157,15 @@ classdef FFE_DCremoval < handle end end - if ~training - figure(1122) - hold on - scatter(1:numel(e_dc_save),e_dc_save,1,'.'); - scatter(1:numel(y),y,1,'.'); - - end + % if ~training + % figure(1122) + % hold on + % scatter(1:numel(e_dc_save),e_dc_save,1,'.'); + % % scatter(1:numel(y),y,1,'.'); + % + % end end end -end - +end \ No newline at end of file diff --git a/Classes/04_DSP/Equalizer/FFE_DCremoval_adaptive_mu.m b/Classes/04_DSP/Equalizer/FFE_DCremoval_adaptive_mu.m new file mode 100644 index 0000000..c6b1a43 --- /dev/null +++ b/Classes/04_DSP/Equalizer/FFE_DCremoval_adaptive_mu.m @@ -0,0 +1,272 @@ +classdef FFE_DCremoval_adaptive_mu < handle + % Implementation of plain and simple FFE. + % 1) Training mode (stable performance when you use NLMS) + % 2) Decision directed mode + + % Eq = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0); + + properties + sps % usually 2 + order + e + error + + len_tr + mu_tr + epochs_tr + + mu_dd + epochs_dd + + mu_dc + dc_buffer_len + + ffe_buffer_len + + smoothing_buffer_length + smoothing_buffer_update + + constellation + + decide + end + + methods + function obj = FFE_DCremoval_adaptive_mu(options) + arguments(Input) + + options.sps = 2; + options.order = 15; + + options.len_tr = 4096; + options.mu_tr = 0; + options.epochs_tr = 5; + + options.mu_dd = 1e-5; + options.epochs_dd = 5; + + options.mu_dc = 0.05; + options.dc_buffer_len = 1; + + options.ffe_buffer_len = 1; + + options.smoothing_buffer_length = 0; + options.smoothing_buffer_update = 0; + options.decide = false; + + end + + assert(options.dc_buffer_len>0); + + fn = fieldnames(options); + for n = 1:numel(fn) + obj.(fn{n}) = options.(fn{n}); + end + + obj.e = zeros(obj.order,1); + obj.error = 0; + + obj.dc_buffer_len = floor(obj.dc_buffer_len); + + end + + function [X,Noi] = 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 obj.smoothing_buffer_length > 0 + % % Apply A1 filter smoothing + % % Calculate the moving sum with the window size N1 + % moving_sum = movsum(X.signal, [obj.smoothing_buffer_length,0]); + % + % % Initialize the output smoothed signal + % X.signal = X.signal - (1 / obj.smoothing_buffer_length) * moving_sum; + % end + + % Training Mode + training = 1; + obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training); + + % Decision Directed Mode + N = X.length; + training = 0; + [signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training); + + % Output Signal + if obj.decide + X.signal = decision; + else + X.signal = signal; + end + 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 + + Noi = X - D; + + end + + function [y,d_hat] = equalize(obj, x, d, mu_lms, epochs, N, training) + % Equalize with adaptive DC-removal, VSS, and parallel-buffered DC updates + % Added: FFE gradient buffering in DD mode (error buffer) with update every obj.dc_buffer_len symbols + + arguments + obj + x + d + mu_lms % LMS step-size (or 0 for NLMS) + epochs % number of training/DD epochs + N % number of samples to process + training % boolean flag: true->training mode, false->DD mode + end + + % Zero-padding for filter memory + x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)]; + + % Initialize storage + numSymbols = ceil(N/obj.sps); + y = zeros(numSymbols,1); + d_hat = zeros(numSymbols,1); + err = NaN(numSymbols,numel(obj.constellation)); + e_dc_save= zeros(numSymbols,1); + + % DC-adaptation parameters + P_err = 0; % running error power + alpha = 0.98; % forgetting factor for error power + err_prev = 0; % previous error sample for VSS correlation + gamma_dc = 1e-6; % meta step-size for DC VSS + mu_min = 1e-6; % lower bound for mu_dc + mu_max = 1e-1; % upper bound for mu_dc + + % DC removal buffer + L = obj.dc_buffer_len; % buffer length + e_dc_buf = NaN(L,1); + e_dc_est = 0; + + % FFE gradient buffer (DD mode only) + L_grad = obj.ffe_buffer_len; % buffer length + if ~training + % each column holds one past gradient of length obj.order + grad_buf = NaN(obj.order, L_grad); + + end + + smth_buffer = zeros(1, obj.smoothing_buffer_length); + smth_mean = 0; + % Main loop + for epoch = 1:epochs + s = 0; + for sample = 1:obj.sps:N + s = s + 1; + + if obj.smoothing_buffer_length > 0 + smth_buffer = circshift(smth_buffer,1,2); + smth_buffer(1) = x(sample); + if mod(s, obj.smoothing_buffer_update) == 0 + smth_mean = mean(smth_buffer); + end + x(sample:sample+obj.sps-1) = x(sample:sample+obj.sps-1)-smth_mean; + end + + U = x(obj.order+sample-1:-1:sample); + + %-- 1) filter output with DC correction + y(s) = e_dc_est + obj.e.'*U; + + %-- 2) decision + if training + [~, idx] = min(abs(d(s) - obj.constellation)); + else + [~, idx] = min(abs(y(s) - obj.constellation)); + end + d_hat(s) = obj.constellation(idx); + + %-- 3) error + e_val = y(s) - d_hat(s); + err(s,idx) = e_val; + + %-- 4) tap-weight update: training immediate, DD buffered + if training + % immediate update (LMS or NLMS) + if mu_lms ~= 0 + obj.e = obj.e - mu_lms * e_val * U; + else + normU = (U.'*U) + eps; + obj.e = obj.e - e_val * U / normU; + end + else + if 1 + % buffer gradient + if mu_lms ~= 0 + grad = e_val * U; + else + normU = (U.'*U) + eps; + grad = e_val * U / normU; + end + % shift and insert + grad_buf = circshift(grad_buf, 1, 2); + grad_buf(:,1) = grad; + % update once every L symbols + if mod(s, L_grad) == 0 + avg_grad = mean(grad_buf, 2, 'omitnan'); + if mu_lms ~= 0 + obj.e = obj.e - mu_lms * avg_grad; + else + obj.e = obj.e - avg_grad; + end + end + end + end + + + %-- 5) DC adaptation + if obj.mu_dc ~= 0 + + % VSS for mu_dc + delta_mu = gamma_dc * e_val * err_prev * (U.'*U); + obj.mu_dc = min(max(obj.mu_dc + delta_mu, mu_min), mu_max); + err_prev = e_val; + + % DC buffer update & periodic estimate + P_err = alpha*P_err + (1-alpha)*e_val^2; + mu_dc_norm = obj.mu_dc / (P_err + eps); + e_dc_buf = circshift(e_dc_buf, 1); + e_dc_buf(1) = e_dc_est - mu_dc_norm * e_val; + + if mod(s, L) == 0 + e_dc_est = median(e_dc_buf, 'omitnan'); + end + + e_dc_save(s) = e_dc_est; + end + + % store instantaneous squared error + obj.error(epoch, s) = e_val^2; + end + end + + % Optional plotting in DD mode (uncomment if needed) + if ~training + figure(342);clf + hold on + scatter(1:numSymbols, err + obj.constellation', '.', 'SizeData', 1); + + yline(obj.constellation); + + end + end + + + function mu = update_mu() + + + + end + + end +end + diff --git a/Classes/04_DSP/Equalizer/FFE_DCremoval_level.m b/Classes/04_DSP/Equalizer/FFE_DCremoval_level.m new file mode 100644 index 0000000..3539d2c --- /dev/null +++ b/Classes/04_DSP/Equalizer/FFE_DCremoval_level.m @@ -0,0 +1,240 @@ +classdef FFE_DCremoval_level < handle + % Implementation of plain and simple FFE. + % 1) Training mode (stable performance when you use NLMS) + % 2) Decision directed mode + + % Eq = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0); + + properties + sps % usually 2 + order + e + error + + len_tr + mu_tr + epochs_tr + + mu_dd + epochs_dd + + mu_dc + dc_buffer_len + + constellation + + decide + + KF_meas_noise = 0; + KF_process_noise = 0; + KF_state_cov = 0; + end + + methods + function obj = FFE_DCremoval_level(options) + arguments(Input) + + options.sps = 2; + options.order = 15; + + options.len_tr = 4096; + options.mu_tr = 0; + options.epochs_tr = 5; + + options.mu_dd = 1e-5; + options.epochs_dd = 5; + + options.mu_dc = 0.05; + options.dc_buffer_len = 1; + + options.decide = false; + + end + + assert(options.dc_buffer_len>0); + + fn = fieldnames(options); + for n = 1:numel(fn) + obj.(fn{n}) = options.(fn{n}); + end + + obj.e = zeros(obj.order,1); + obj.error = 0; + + obj.dc_buffer_len = floor(obj.dc_buffer_len); + + end + + function [X,Noi] = 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); + + % Training Mode + training = 1; + obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training); + + % Decision Directed Mode + N = X.length; + training = 0; + [signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training); + + % Output Signal + if obj.decide + X.signal = decision; + else + X.signal = signal; + end + 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 + + Noi = X - D; + + end + function [y, d_hat, logs] = equalize(obj, x, d, mu_lms, epochs, N, training) + % Equalize with Kalman-based DC removal; training epochs estimate KF noise parameters + + arguments + obj + x + d + mu_lms % LMS step-size (or 0 for NLMS) + epochs % number of training or DD epochs + N % number of samples to process + training % true => training mode (tap-training + noise estimation) + end + + % Zero-pad for filter memory + x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)]; + numSym = ceil(N/obj.sps); + + % Pre-allocate outputs + y = zeros(numSym,1); + d_hat = zeros(numSym,1); + + % --- Training: estimate noise stats and train taps --- + if training + % Pre-allocate error accumulator + totalTrain = epochs * numSym; + trainErrs = zeros(totalTrain,1); + te_idx = 0; + + for ep = 1:epochs + s = 0; + for n = 1:obj.sps:N + s = s + 1; + U = x(obj.order + n - 1 : -1 : n); + + % Equalizer output (no DC correction yet) + y(s) = obj.e.' * U; + + % Decision based on known symbol + [~, idx] = min(abs(d(s) - obj.constellation)); + d_hat(s) = obj.constellation(idx); + + % Instantaneous error + e_n = y(s) - d_hat(s); + + % Collect error for noise estimation + te_idx = te_idx + 1; + trainErrs(te_idx) = e_n; + + % Tap-weight update (LMS or NLMS) + if mu_lms ~= 0 + obj.e = obj.e - mu_lms * e_n * U; + else + normU = (U.'*U) + eps; + obj.e = obj.e - e_n * U / normU; + end + end + end + + % Estimate measurement noise R and process noise Q + R_est = var(trainErrs(1:te_idx)); + Q_est = 1e-3 * R_est; % Q/R ratio = 1e-3 (tune as needed) + + % Store into object for DD pass + obj.KF_meas_noise = R_est; + obj.KF_process_noise = Q_est; + obj.KF_state_cov = 5*R_est; % or 5*R_est for a more “eager” start + + % No Kalman in training; return + logs = struct(); + return; + end + + % --- Decision-Directed with Kalman DC tracking --- + % Initialize Kalman state + x_est = 0; + P = obj.KF_state_cov; % initial P (tune in obj; e.g. 1) + + % Logging containers + logs.y_raw = zeros(numSym,1); + logs.y_corr = zeros(numSym,1); + logs.err = zeros(numSym,1); + logs.K_gain = zeros(numSym,1); + logs.x_est = zeros(numSym,1); + logs.P = zeros(numSym,1); + logs.normU = zeros(numSym,1); + logs.tap_norm = zeros(numSym,1); + + for ep = 1:epochs + s = 0; + for n = 1:obj.sps:N + s = s + 1; + U = x(obj.order + n - 1 : -1 : n); + + % 1) Kalman prediction + P = P + obj.KF_process_noise; + x_prior = x_est; + + % 2) raw equalizer output + y_raw = obj.e.' * U; + logs.y_raw(s) = y_raw; + + % 3) DC-corrected output + y_corr = y_raw + x_prior; + y(s) = y_corr; + logs.y_corr(s) = y_corr; + + % 4) decision-directed symbol + [~, idx] = min(abs(y_corr - obj.constellation)); + d_hat(s) = obj.constellation(idx); + + % 5) error + e_n = y_corr - d_hat(s); + logs.err(s) = e_n; + + % 6) tap-weight update (LMS/NLMS) + if mu_lms ~= 0 + obj.e = obj.e - mu_lms * e_n * U; + else + normU = U.' * U + eps; + logs.normU(s) = normU; + obj.e = obj.e - e_n * U / normU; + end + + logs.tap_norm(s) = norm(obj.e); + + % 7) Kalman update + K_gain = P / (P + obj.KF_meas_noise); + x_est = x_prior + K_gain * (e_n - x_prior); + P = (1 - K_gain) * P; + + % 8) log Kalman state + logs.K_gain(s) = K_gain; + logs.x_est(s) = x_est; + logs.P(s) = P; + end + end + end + + + + end +end + diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index 5e8333e..6a77aec 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -70,7 +70,7 @@ classdef DBHandler < handle % Get table names and the first rows of each table to understand the structure obj.getTableNames(); obj.getTables(); - obj.getDistinctValues(); + % obj.getDistinctValues(); end function obj = getTableNames(obj) diff --git a/Functions/EQ_structures/vnle.m b/Functions/EQ_structures/vnle.m index 1447351..f143452 100644 --- a/Functions/EQ_structures/vnle.m +++ b/Functions/EQ_structures/vnle.m @@ -1,129 +1,192 @@ function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options) - %VNLE Apply an equalization algorithm to the received signal and calculate BER - % This function takes an equalizer object, a received signal, and the - % transmitted symbols to apply equalization, map the received signal back to bits, - % and compute the bit error rate (BER). - % - % Inputs: - % EQ - Equalizer object that provides the equalization method - % rx_signal - Received signal that needs to be equalized - % tx_symbols - Transmitted symbols used as a reference for BER calculation - % - % Outputs: - % eq_signal - Equalized version of the received signal - % ber - Bit error rate after equalization - % numErrors - Number of bit errors detected - arguments - eq_ - M - rx_signal - tx_symbols - tx_bits - options.precode_mode db_mode - options.showAnalysis = 0 - options.eth_style = 0; - options.postFFE = []; - end +arguments + eq_ + M + rx_signal + tx_symbols + tx_bits + options.precode_mode db_mode + options.showAnalysis = 0; + options.eth_style_symbol_mapping = 0; + options.postFFE = []; + options.database = []; +end - %FFE or VNLE - if length(rx_signal)/2 == length(tx_symbols)+0.5 - rx_signal.signal = rx_signal.signal(1:end-1); - elseif length(rx_signal)/2 == length(tx_symbols)-1 - end - [eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols); +mudc_given = eq_.mu_dc; - if ~isempty(options.postFFE) - [eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols); - end +%FFE or VNLE +[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols); - eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd); +if ~isempty(options.postFFE) + tic + [eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols); + toc +end - % precoding to mitigate error propagation, most prominently used in - % combination with duobinary signaling to avoid catastrophic error - % behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling) +eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd); - % takes: - % -> eq_signal_hd: hard decision signal after eq - % -> tx_symbols: that where used as reference for eq +% precoding to mitigate error propagation, most prominently used in +% combination with duobinary signaling to avoid catastrophic error +% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling) - switch options.precode_mode - case db_mode.db_emulate - % re - eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M); - eq_signal_hd = Duobinary().decode(eq_signal_hd,"M",M); +switch options.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 = PAMmapper(M,0,"eth_style",options.eth_style).demap(tx_symbols_precoded); + tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); - case db_mode.db_discard + rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_precoded); + [~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle.signal,tx_bits_precoded.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1); - % normal dsp for precoded sequence == discard/omit/ignore precode - tx_bits = PAMmapper(M,0).demap(tx_symbols); + %B) Just determine BER + rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); + [bits_vnle,errors_vnle,ber_vnle,error_pos_vnle] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1); - case db_mode.db_encoded + max_burst_length = 10; + burst_count = count_error_bursts(error_pos_vnle, max_burst_length); - % normal DB encoded data (only for 10KM) + case db_mode.db_precoded - case db_mode.db_precoded + % Daten SIND TATSÄCHLICH precoded auf TX Seite: - eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M); - eq_signal_hd = Duobinary().decode(eq_signal_hd,"M",M); + % A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here! + 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_vnle_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_decoded); + [~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle_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 = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols); + rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); + [bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1); + + +end + +% METRICS OF VNLE SD Signal: +[snr_vnle,snr_vnle_lvl] = calc_snr(tx_symbols.signal,eq_noise.signal); +[gmi_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000); +air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M)); +[evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols); +[std_vnle_total,std_vnle_lvl] = calc_std(eq_signal_sd,tx_symbols); +[std_rxraw_total,std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols); + +eq_package.ber_vnle = ber_vnle; +eq_package.evm_vnle_total = evm_vnle_total; +eq_package.evm_vnle_lvl = evm_vnle_lvl; +eq_package.gmi = gmi_vnle; + +eq_package.eq = eq_; + +resultsVNLE = struct( ... + 'result_id', NaN, ... % + 'run_id', NaN, ... % Beispielhafte Run-ID + 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle + 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit + 'BER', ber_vnle, ... % BER = 120 / 1.000.000 + 'numBits', bits_vnle, ... % Beispiel: 1.000.000 Bits + 'numBitErr', errors_vnle, ... % Beispiel: 120 Bitfehler + 'BER_precoded', ber_vnle_diff_precoded, ... % BER = 120 / 1.000.000 + 'numBitErr_precoded', errors_vnle_diff_precoded, ... % Beispiel: 120 Bitfehler + 'SNR', snr_vnle, ... % Beispielhafte SNR + 'SNR_level', jsonencode(snr_vnle_lvl), ... % SNR-Level als JSON-codiertes Array + 'STD', std_vnle_total, ... + 'STD_level', jsonencode(std_vnle_lvl),... + 'STDrx' , std_rxraw_total, ... + 'STDrx_level', jsonencode(std_rxraw_lvl),... + 'GMI', gmi_vnle, ... % Beispielhafter GMI-Wert + 'AIR', air_vnle, ... % Beispielhafter AIR-Wert + 'EVM', evm_vnle_total, ... % Beispielhafte EVM + 'EVM_level', jsonencode(evm_vnle_lvl), ... % EVM-Level als JSON-codiertes Array + 'Alpha', [] ... % Beispielhafter Alpha-Wert + ); + +if ~isempty(options.postFFE) + npostFFE = options.postFFE.order; +else + npostFFE = 0; +end + +equalizerConfigVNLE = struct( ... + 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt + 'equalizer_structure', int32(equalizer_structure.vnle), ... % Beispiel: 1 (z.B. für vnle) + 'M', M, ... % Ordnung der PAM-Konstellation + 'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String + 'db_target', 0, ... % 0 oder 1 + 'diff_precode', int32(options.precode_mode), ... % 0 oder 1 + 'postFFE', int32(~isempty(options.postFFE)), ... % Beispielwert + 'NpostFFE', npostFFE, ... % Beispielwert + 'Ne1', eq_.order, ... % Feedforward Koeffizienten 1. Ordnung + 'K', eq_.sps, ... % Samples pro Symbol + 'DCmu', mudc_given, ... % Anpassungsrate für DC-Tap + 'training_length', eq_.len_tr, ... % Anzahl Trainingssymbole + 'training_loops', eq_.epochs_tr, ... % Anzahl Trainingsdurchläufe + 'TRmu1', eq_.mu_tr, ... % mu für DD-Modus (1. Ordnung) + 'dd_loops', eq_.epochs_dd, ... % Anzahl Durchläufe im DD-Modus + 'DDmu1', eq_.mu_dd, ... % mu für DD-Modus (1. Ordnung) + 'comment', 'function: ffe dc removal', ... % Zusätzliche Kommentare + 'ffe_buffer_len', eq_.ffe_buffer_len, ... + 'smoothing_buffer_len', eq_.smoothing_buffer_length, ... + 'smoothing_buffer_update', eq_.smoothing_buffer_update, ... + 'dc_buffer_len', eq_.dc_buffer_len, ... + 'config_hash', NaN ... + ); + +eq_package.resultsVNLE = resultsVNLE; +eq_package.equalizerConfigVNLE = equalizerConfigVNLE; + + +% eq_package.vnle_out = eq_signal_sd; +if options.showAnalysis + + + % fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl); + + % fprintf('VNLE BER: %.2e \n',ber_vnle); + % + % fprintf('MLSE BER: %.2e \n',ber_mlse); + + figure(336); + showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE'); + + % figure(337);clf; + % rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal'); + + % figure(338);clf; + % showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients','fignum',338); + + if ~isempty(options.postFFE) + showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients','fignum',338); end - rx_bits = PAMmapper(M,0,"eth_style",options.eth_style).demap(eq_signal_hd); + showEQfilter(eq_.e,eq_signal_sd.fs.*2); - [~,numErrors,ber,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - [evm_total,evm_lvl] = calc_evm(eq_signal_sd,tx_symbols); - [inf_rate] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000); + % figure(340);clf; + % eq_signal_sd.eye(eq_signal_sd.fs,M,"fignum",340); + + figure(341);clf; + showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",341); + + figure(400);clf; + warning off + showLevelScatter(eq_signal_sd,tx_symbols,"fignum",400); + + showLevelScatter(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols,"fignum",400); + warning on + % autoArrangeFigures(3,3,2) - eq_package.ber_vnle = ber; - eq_package.evm_total = evm_total; - eq_package.evm_lvl = evm_lvl; - eq_package.inf_rate_vnle = inf_rate; - eq_package.snr = snr(eq_signal_sd.signal,eq_noise.signal); +end - eq_package.signal = eq_signal_sd; +end - - if options.showAnalysis - - fprintf('SNR: %d dB \n',snr(eq_signal_sd.signal,eq_noise.signal)); - - if M == 6 - logm = 2.5; - else - logm = log2(M); - end - - fprintf('NGMI: %.4f \n', inf_rate/logm); - - fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl); - - fprintf('VNLE BER: %.2e \n',ber); - - disp("%%%%%%%%%%%%%%%%%%%%%") - - % showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients'); - % - % if ~isempty(options.postFFE) - % showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients'); - % end - % - % showEQNoisePSD(eq_noise); - % - % showEQfilter(eq_.e,eq_signal_sd.fs.*2) - - % noiselessness(tx_symbols,eq_noise,"displayname",'SNR after VNLE','fignum',301); - - % showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",302); - - - - end - -end \ No newline at end of file diff --git a/Functions/EQ_structures/vnle_postfilter_mlse.m b/Functions/EQ_structures/vnle_postfilter_mlse.m index d125ca6..85a17e1 100644 --- a/Functions/EQ_structures/vnle_postfilter_mlse.m +++ b/Functions/EQ_structures/vnle_postfilter_mlse.m @@ -100,7 +100,7 @@ end air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M)); [evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols); [std_vnle_total,std_vnle_lvl] = calc_std(eq_signal_sd,tx_symbols); -[std_rxraw_total,std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols); +[std_rxraw_total,std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols); % METRICS OF MLSE (HD-VITERBI) pf_.ncoeff = 1; @@ -252,7 +252,7 @@ if options.showAnalysis % % fprintf('MLSE BER: %.2e \n',ber_mlse); - figure(336);clf; + figure(336); showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE','postfilter_taps',pf_.coefficients); % figure(337);clf; @@ -273,9 +273,12 @@ if options.showAnalysis figure(341);clf; showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",341); + warning off showLevelScatter(eq_signal_sd,tx_symbols,"fignum",400); showLevelScatter(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols,"fignum",401); + warning on + drawnow; % autoArrangeFigures(3,3,2) diff --git a/Functions/EQ_visuals/showLevelScatter.m b/Functions/EQ_visuals/showLevelScatter.m index b7cab41..1bc7e33 100644 --- a/Functions/EQ_visuals/showLevelScatter.m +++ b/Functions/EQ_visuals/showLevelScatter.m @@ -20,7 +20,6 @@ if isnan(options.fignum) fig = figure; % Create a new figure and get its handle else fig = figure(options.fignum); % Use the specified figure number - clf; end assert(~isempty(options.f_sym),'No fsym given'); @@ -40,7 +39,7 @@ ende = length(correct_symbols); % start = 30000; % ende = 40000; -for l = 1:4 +for l = 1:numel(levels) ccnt = ccnt+2; level_amplitude = levels(l); @@ -50,23 +49,24 @@ for l = 1:4 std_lvl(l) = std(symbols_for_lvl,'omitnan'); xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6; % xax_in_sec = 1:length(correct_symbols); - +if 0 scatter(xax_in_sec(start:ende),symbols_for_lvl(start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:)); hold on; end +end std_lvl = round(std_lvl,2); ccnt = 0; % Add the windowed/ smoothed curves -for l = 1:4 +for l = 1:numel(levels) ccnt = ccnt+2; level_amplitude = levels(l); symbols_for_lvl = NaN(1,length(correct_symbols)); - movmean = 1/250 .* movsum(rx_symbols(correct_symbols==level_amplitude),[250/2,250/2]); + movmean = 1/250 .* movsum(rx_symbols(correct_symbols==level_amplitude),[250/2,250/2], 'Endpoints', 'fill'); symbols_for_lvl(correct_symbols==level_amplitude) = movmean; diff --git a/Functions/Metrics/count_error_bursts.m b/Functions/Metrics/count_error_bursts.m new file mode 100644 index 0000000..fdc5fc1 --- /dev/null +++ b/Functions/Metrics/count_error_bursts.m @@ -0,0 +1,29 @@ +function burst_count = count_error_bursts(err_pos, max_burst_length) + % err_pos: Vector of error positions + % max_burst_length: Maximum length for which bursts are counted (e.g., 10) + + % Sort the error positions to ensure they're in increasing order + err_pos = sort(err_pos); + + % Initialize burst_count array to hold the counts for each burst length + burst_count = zeros(1, max_burst_length); + + % Find the differences between consecutive error positions + diffs = diff(err_pos); + + % Find the start of the bursts (where the difference is greater than 1) + burst_starts = [1, find(diffs > 1) + 1]; % Starts at index 1 and after gaps + burst_ends = [find(diffs > 1), length(err_pos)]; % Ends where gaps are + + % Loop over the bursts + for b = 1:length(burst_starts) + burst_length = burst_ends(b) - burst_starts(b) + 1; + + % Check if the burst length exceeds any of the thresholds + for i = 1:max_burst_length + if burst_length > i + burst_count(i) = burst_count(i) + 1; + end + end + end +end diff --git a/projects/ECOC_2025/dsp_loop_id.m b/projects/ECOC_2025/dsp_loop_id.m new file mode 100644 index 0000000..2d9ab8f --- /dev/null +++ b/projects/ECOC_2025/dsp_loop_id.m @@ -0,0 +1,63 @@ + + + +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', [], ... + 'interference_path_length', 10, ... + 'is_mpi', 1, ... + 'pam_level', 4, ... + 'wavelength', 1310, ... + 'precomp_amp', [], ... + 'signal_attenuation', [], ... + 'v_awg', [], ... + 'v_bias', 2.65 ... + ); + +selectedFields = {'Runs.run_id','Runs.loop_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', 'Configurations.interference_path_length'}; + +[dataTable,sql_query] = db.queryDB(filterParams, selectedFields); + +dataTable(dataTable.loop_id~=217,:) = []; + +num_occ = 10; +run_par = true; +run_id = dataTable.run_id; + +params.dc_buffer_len = 224; +params.ffe_buffer_len = 1; +params.smoothing_buffer_length = 0; +params.smoothing_buffer_update = 0; +params.mu_dc = 0.005; + +futures_list = parallel.FevalFuture.empty(); +for id = 1:length(dataTable.run_id) + run_id = dataTable.run_id(id); + [out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params); +end + +% Extract all ber_mlse values from the vnle_pf_package using cellfun +ber_mlse = cellfun(@(pkg) pkg.ber_mlse, future.OutputArguments{1,1}.vnle_pf_package); +ber_vnle = cellfun(@(pkg) pkg.ber_vnle, future.OutputArguments{1,1}.vnle_pf_package); + +figure(101) +hold on; +scatter(1:num_occ,ber_mlse,15,'Marker','*'); +scatter(1:num_occ,ber_vnle,15,'Marker','*'); +legend('Interpreter', 'latex'); +xlabel('Occurences'); +ylabel('BER'); +grid on; +beautifyBERplot; diff --git a/projects/ECOC_2025/dsp_run_id.m b/projects/ECOC_2025/dsp_run_id.m index 2b57c8e..5ef32d3 100644 --- a/projects/ECOC_2025/dsp_run_id.m +++ b/projects/ECOC_2025/dsp_run_id.m @@ -48,15 +48,14 @@ mu_ffe3 = 0.001; mu_dfe = 0.0004; mu_dc = 0.00; -% mu_ffe1 = 0; -% mu_ffe2 = 0; -% mu_ffe3 = 0; -% mu_dfe =0; -% mu_dc = 0.00; - mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3]; vnle_order=[vnle_order1,vnle_order2,vnle_order3]; +dc_buffer_len = 224; +ffe_buffer_len = 1; +smoothing_buffer_length = 4096; +smoothing_buffer_update = 224; + % Overwrite default parameters if given in options.parameters paramStruct = options.parameters; if ~isempty(paramStruct) @@ -78,7 +77,7 @@ eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0 output = struct(); vnle_pf_package = {}; -vnle_dfe_package = {}; +vnle_package = {}; dbtgt_package = {}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -121,7 +120,7 @@ if ~found_sync end record_realizations = min(options.max_occurences,length(Scpe_cell)); -for occ = 1:record_realizations +for occ = 4:record_realizations Scpe_sig = Scpe_cell{occ}; @@ -137,16 +136,27 @@ for occ = 1:record_realizations if duob_mode ~= db_mode.db_encoded - vnle_pf = 1; + vnle_pf = 0; dbtgt = 0; % %%%%% VNLE + DFE %%%% - if 0 + if 1 - eq_vnle_dfe = EQ("Ne",vnle_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); - eq_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); + eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... + 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,... + "mu_dc",mu_dc,... + "dc_buffer_len",dc_buffer_len, ... + "ffe_buffer_len",ffe_buffer_len,... + "smoothing_buffer_length",smoothing_buffer_length,... + "smoothing_buffer_update",smoothing_buffer_update); - [result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,"showAnalysis",1,"postFFE",[]); - vnle_dfe_package{occ} = result; + result = vnle(eq_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0); + vnle_package{occ} = result; + fprintf("FFE Results: %.2e\n", result.ber_vnle); + + + if options.append_to_db + database.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE); + end end @@ -176,32 +186,32 @@ for occ = 1:record_realizations % % fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e \n",vnle_pf_package{occ}.resultsVNLE.BER,vnle_pf_package{occ}.resultsVNLE.BER_precoded ,vnle_pf_package{occ}.resultsMLSE.BER,vnle_pf_package{occ}.resultsMLSE.BER_precoded); if vnle_pf - occ = 1; % or whatever your loop index is + % occ = 1; % or whatever your loop index is % Extract VNLE results for readability - vnle = vnle_pf_package{occ}.resultsVNLE; - mlse = vnle_pf_package{occ}.resultsMLSE; + vnle_result = vnle_pf_package{occ}.resultsVNLE; + mlse_result = vnle_pf_package{occ}.resultsMLSE; % Print header fprintf("==== EQUALIZATION RUN-ID %d | PAM-%d | %.2f GBd ====\n\n", run_id, M, Symbols.fs.*1e-9); - +s % VNLE Results fprintf(">> VNLE Results:\n"); - fprintf(" BER %.2e\n", vnle.BER); - fprintf(" BER (pre-code) %.2e\n", vnle.BER_precoded); - fprintf(" SNR: %.2f dB\n", vnle.SNR); - fprintf(" GMI: %.4f\n", vnle.GMI); + fprintf(" BER %.2e\n", vnle_result.BER); + fprintf(" BER (pre-code) %.2e\n", vnle_result.BER_precoded); + fprintf(" SNR: %.2f dB\n", vnle_result.SNR); + fprintf(" GMI: %.4f\n", vnle_result.GMI); fprintf(" Linerate: %.2f Gbps\n", Symbols.fs .* floor(log2(M)*10)/10 .*1e-9); - fprintf(" AIR: %.2f Gbps\n", vnle.AIR.*1e-9); + fprintf(" AIR: %.2f Gbps\n", vnle_result.AIR.*1e-9); fprintf("\n"); % MLSE Results fprintf(">> MLSE Results:\n"); - fprintf(" BER : %.2e\n", mlse.BER); - fprintf(" BER (pre-code): %.2e\n", mlse.BER_precoded); - fprintf(" Channel Alpha : %.2f\n", mlse.Alpha); + fprintf(" BER : %.2e\n", mlse_result.BER); + fprintf(" BER (pre-code): %.2e\n", mlse_result.BER_precoded); + fprintf(" Channel Alpha : %.2f\n", mlse_result.Alpha); fprintf("\n"); end @@ -244,6 +254,7 @@ end output.dataTable = dataTable; output.vnle_pf_package = vnle_pf_package; +output.vnle_package = vnle_package; output.dbtgt_package = dbtgt_package; end \ No newline at end of file diff --git a/projects/ECOC_2025/dsp_standalone.m b/projects/ECOC_2025/dsp_standalone.m index 56ba875..b28e6ff 100644 --- a/projects/ECOC_2025/dsp_standalone.m +++ b/projects/ECOC_2025/dsp_standalone.m @@ -4,9 +4,89 @@ 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("pathToDB", [databasePath, database_name],"type","mysql"); -% db = DBHandler("type","mysql"); -num_occ = 5; -run_par = false; -run_id = 1958; -[out, future] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ); +db = DBHandler("type","mysql"); +% db = DBHandler("pathToDB", [databasePath, database_name],"type","sqlite"); + +filterParams = db.tables; +% filterParams.Configurations = struct('run_id', 4001); +filterParams.Configurations = struct( ... + 'symbolrate', 112e9, ... %[224,336,360,390,420,448] + 'fiber_length', 0, ... + 'db_mode', '"no_db"', ... + 'interference_attenuation', [], ... + 'interference_path_length', [], ... + 'is_mpi', 1, ... + 'pam_level', 4, ... + 'wavelength', 1310, ... + 'precomp_amp', [], ... + 'signal_attenuation', [], ... + 'v_awg', [], ... + 'v_bias', 2.65 ... + ); + +selectedFields = {'Runs.run_id','Runs.loop_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', 'Configurations.interference_path_length'}; + +[dataTable,sql_query] = db.queryDB(filterParams, selectedFields); + +% dataTable(dataTable.loop_id<200,:) = []; + +num_occ = 15; +run_par = true; +run_id = dataTable.run_id; +params = struct(); + +% slow DC tracking +params.dc_buffer_len = 224; +params.ffe_buffer_len = 1; +params.smoothing_buffer_length = 0; +params.smoothing_buffer_update = 0; +params.mu_dc = 0.005; + +futures_list = parallel.FevalFuture.empty(); +for id = 1:length(dataTable.run_id) + run_id = dataTable.run_id(id); + [out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params); +end + +% ideal DC tracking +params.dc_buffer_len = 1; +params.ffe_buffer_len = 1; +params.smoothing_buffer_length = 0; +params.smoothing_buffer_update = 0; +params.mu_dc = 0.005; + +futures_list = parallel.FevalFuture.empty(); +for id = 1:length(dataTable.run_id) + run_id = dataTable.run_id(id); + [out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params); +end + +% DC smoothing +params.dc_buffer_len = 1; +params.ffe_buffer_len = 1; +params.smoothing_buffer_length = 4096; +params.smoothing_buffer_update = 224; +params.mu_dc = 0.00; + +futures_list = parallel.FevalFuture.empty(); +for id = 1:length(dataTable.run_id) + run_id = dataTable.run_id(id); + [out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params); +end + +% only FFE +params.dc_buffer_len = 1; +params.ffe_buffer_len = 1; +params.smoothing_buffer_length = 0; +params.smoothing_buffer_update = 0; +params.mu_dc = 0.00; + +futures_list = parallel.FevalFuture.empty(); +for id = 1:length(dataTable.run_id) + run_id = dataTable.run_id(id); + [out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params); +end + + diff --git a/projects/ECOC_2025/dsp_test/hyperparam_tuning.m b/projects/ECOC_2025/dsp_test/hyperparam_tuning.m new file mode 100644 index 0000000..75f415e --- /dev/null +++ b/projects/ECOC_2025/dsp_test/hyperparam_tuning.m @@ -0,0 +1,58 @@ + + +Scpe_sig = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_scopesignal.mat");Scpe_sig = Scpe_sig.Scpe_sig; +Tx_bits = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_bits.mat");Tx_bits = Tx_bits.Tx_bits; +Symbols = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_symbols.mat");Symbols = Symbols.Symbols; + +eq_ = FFE_DCremoval("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",1e-5,"mu_tr",0,"order",50,"sps",2,"decide",0,"mu_dc",0.005,"dc_buffer_len",1); + +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"); + +params = (logspace(-6,-2,20)); +params = floor((logspace(2,3,20))); +params = 224; + +for i = 1:length(params) + + eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... + 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,... + "mu_dc",0.005,"dc_buffer_len",224, ... + "ffe_buffer_len",1,... + "smoothing_buffer_length",0,... + "smoothing_buffer_update",224); + + result = vnle(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0); + ber_ffe(i) = result.ber_vnle; + fprintf(" FFE Results: %.2e\n", ber_ffe(i)); + + db.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE); + + % eq_ = FFE_adaptive_decision("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... + % 0.0003,"mu_tr",0,"order",50,"sps",2,"decide",1,"buffer_length",params(i)); + % + % result = vnle(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0); + % ber_dc(i) = result.ber_vnle; + % + % fprintf(" FFE+dc tr. Results: %.2e\n", ber_dc(i)); + +end + +figure(103435) +hold on; +% scatter(params,ber_dc,15,'Marker','o','LineWidth',1,'DisplayName','DC tracking'); +% [a,b]=min(ber_dc); +% scatter(params(b),a,45,'Marker','x','MarkerEdgeColor','r','LineWidth',1); + +scatter(params,ber_ffe,15,'Marker','square','LineWidth',1); +[a,b]=min(ber_ffe); +scatter(params(b),a,45,'Marker','x','MarkerEdgeColor','r','LineWidth',1,'DisplayName','FFE'); + +legend('Interpreter', 'latex'); +xlabel('Occurences'); +ylabel('BER'); +grid on; +beautifyBERplot; +ylim([1e-4,0.1 ]) \ No newline at end of file diff --git a/projects/ECOC_2025/load_signal_standalone.m b/projects/ECOC_2025/load_signal_standalone.m index 51ae8bd..cb27584 100644 --- a/projects/ECOC_2025/load_signal_standalone.m +++ b/projects/ECOC_2025/load_signal_standalone.m @@ -1,6 +1,6 @@ -run_id = 231; +run_id = 3966; savePath = 'Z:\2025\ECOC Silas\ecoc_2025\'; databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; @@ -9,7 +9,22 @@ 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('run_id', run_id); +filterParams.Configurations = struct( ... + 'symbolrate', [], ... %[224,336,360,390,420,448] + 'fiber_length', 0, ... + 'db_mode', '"no_db"', ... + 'interference_attenuation', 4, ... + 'interference_path_length', 10, ... + 'is_mpi', 1, ... + 'pam_level', [], ... + 'wavelength', 1310, ... + 'precomp_amp', [], ... + 'signal_attenuation', [], ... + 'v_awg', [], ... + 'v_bias', 2.65 ... + ); + 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',... @@ -18,29 +33,36 @@ selectedFields = {'Runs.run_id','Runs.tx_bits_path','Runs.tx_signal_path','Runs. [dataTable,sql_query] = db.queryDB(filterParams, selectedFields); [~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices -dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each run_id -fsym = dataTable.symbolrate; -M = double(dataTable.pam_level); -duob_mode = db_mode.(strrep(char(dataTable.db_mode),'"','')); -Tx_signal = load([savePath, char(dataTable.tx_signal_path)]); -Tx_signal = Tx_signal.Digi_sig; +for i = 1:4%size(dataTable,1) + dataTable_ = dataTable(i,:); % Extract unique configurations for each run_id + + fsym = dataTable_.symbolrate; + M = double(dataTable_.pam_level); + duob_mode = db_mode.(strrep(char(dataTable_.db_mode),'"','')); + + Tx_signal = load([savePath, char(dataTable_.tx_signal_path)]); + Tx_signal = Tx_signal.Digi_sig; -Tx_bits = load([savePath, char(dataTable.tx_bits_path)]); -Tx_bits = Tx_bits.Bits; -Symbols_mapped = PAMmapper(M,0).map(Tx_bits); -Symbols_mapped.fs = dataTable.symbolrate; + Tx_bits = load([savePath, char(dataTable_.tx_bits_path)]); + Tx_bits = Tx_bits.Bits; + Symbols_mapped = PAMmapper(M,0).map(Tx_bits); + Symbols_mapped.fs = dataTable_.symbolrate; -Symbols = load([savePath, char(dataTable.tx_symbols_path)]); -Symbols = Symbols.Symbols; + Symbols = load([savePath, char(dataTable_.tx_symbols_path)]); + Symbols = Symbols.Symbols; + + 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 = load([savePath, char(dataTable.rx_raw_path(1))]); -Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; -Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",2*fsym); -[~,Scpe_cell,found_sync,~,shifts] = Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1); -shifts_mus = shifts./Scpe_sig_resampled.fs .*1e6; -Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(run_id)],"fignum",2024,"clear",1); -hold on; -xline(shifts_mus,'HandleVisibility','off'); \ No newline at end of file + Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",2*fsym); + [~,Scpe_cell,found_sync,~,shifts] = Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable_.symbolrate,"debug_plots",1); + + shifts_mus = shifts./Scpe_sig_resampled.fs .*1e6; + Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0); + hold on; + xline(shifts_mus,'HandleVisibility','off'); +end \ No newline at end of file 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 26a85c6..eadb53c 100644 --- a/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m +++ b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m @@ -1,67 +1,129 @@ -local = 1; - -if local - databasePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; -else - databasePath = '\\ntserver.tf.uni-kiel.de\scratch\sioe\ECOC_2025\'; -end - -database_name = 'ecoc2025_loops.db'; -database = DBHandler("pathToDB", [databasePath, database_name]); - -figure(); -plotBoundaries = 1; -plotRealizations = 1; -cols = linspecer(3); % Ensure color count matches -% cols = cols(8,:); +basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +% database = DBHandler("pathToDB",[basePath,'silas_labor.db']); +database = DBHandler("type",'mysql'); 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', 10, ... + 'interference_path_length', [], ... 'is_mpi', 1, ... 'pam_level', 4, ... 'wavelength', 1310, ... 'precomp_amp', [], ... 'signal_attenuation', [], ... - 'v_awg', 0.95, ... - 'v_bias', 2.5 ... + 'v_awg', [], ... + 'v_bias', [] ... ); -% filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded); -% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); + +% 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 selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.v_bias' 'Configurations.v_awg' 'Configurations.precomp_amp' 'Configurations.symbolrate' 'Configurations.pam_level'... 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'Configurations.interference_path_length' 'Configurations.signal_attenuation' ... - 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' ... - 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'}; + '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,sql_query] = database.queryDB(filterParams, selectedFields); -dataTable.SIR = round(-6 - dataTable.power_mpi_interference); -dataTable = cleanUpTable(dataTable); +% [dataTable_raw,sql_query] = database.queryDB(filterParams, selectedFields); + +%% + + + + +dataTable_clean = dataTable_raw; +dataTable_clean.SIR = round(-7.5 - dataTable_clean.power_mpi_interference); +dataTable_clean.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level); +dataTable_clean = cleanUpTable(dataTable_clean); + +%% + +dataTable = dataTable_clean; + +figure(26); +plotBoundaries = 1; +plotRealizations = 0; +cols = linspecer(8); % Ensure color count matches + +% ideal DC tracking +if 0 + dataTable = dataTable(dataTable.dc_buffer_len == 1, :); + dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); + dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); + dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); + dataTable = dataTable(dataTable.DCmu == 0.005, :); + cols = cols(1:1+1,:); + method = 'ideal dc tracking'; + % slow DC tracking +elseif 1 + + dataTable = dataTable(dataTable.dc_buffer_len == 224, :); + dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); + dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); + dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); + dataTable = dataTable(dataTable.DCmu == 0.005, :); + cols = cols(2:2+1,:); + method = 'parallelized dc tracking'; + % slow DC smoothing +elseif 0 + + dataTable = dataTable(dataTable.dc_buffer_len == 1, :); + dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); + dataTable = dataTable(dataTable.smoothing_buffer_len == 4096, :); + dataTable = dataTable(dataTable.smoothing_buffer_update == 224, :); + dataTable = dataTable(dataTable.DCmu == 0, :); + cols = cols(3:3+1,:); + method = 'dc smoothing'; +elseif 0 + % No compensation method + dataTable = dataTable(dataTable.dc_buffer_len == 1, :); + dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); + dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); + dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); + dataTable = dataTable(dataTable.DCmu == 0, :); + cols = cols(4:4+1,:); + method = 'ffe only'; +end dataTable(dataTable.eq_id==0,:) = []; +dataTable(dataTable.equalizer_structure~=1,:) = []; + +% dataTable.interference_path_length(dataTable.interference_path_length < 20, :) = 20; + +dataTable = dataTable(dataTable.interference_path_length == 1000, :); + +% dataTable(dataTable.interference_path_length ~= 50, :) = []; +% dataTable = dataTable(dataTable.interference_path_length < 51, :); + +% dataTable(dataTable.loop_id<200,:) = []; % Filter by time filter_by_time = 0; if filter_by_time - startTime = datetime('2025-04-14 13:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); - stopTime = datetime('2025-04-14 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); + startTime = datetime('2025-04-20 18:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); + stopTime = datetime('2025-04-30 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); dataTable.date_of_run = datetime(dataTable.date_of_run, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); dataTable.date_of_processing = datetime(dataTable.date_of_processing, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); dataTable = dataTable(dataTable.date_of_processing > startTime, :); dataTable = dataTable(dataTable.date_of_processing < stopTime, :); end + % Group by smth y_var = 'BER'; x_var = 'SIR'; -loop_var = 'eq_id'; -fixedVars = {'eq_id',x_var}; +loop_var = 'interference_path_length'; +fixedVars = {'equalizer_structure','interference_path_length',x_var}; [dataTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var); @@ -69,14 +131,13 @@ dataTableGrpd_mean = groupIt(fixedVars, dataTable, @mean); dataTableGrpd_min = groupIt(fixedVars, dataTable, @min); dataTableGrpd_max = groupIt(fixedVars, dataTable, @max); - % Create a new figure mkr = '.'; hold on unique_loop_var = unique(dataTable.(loop_var)); -for i = 1%:numel(unique_loop_var) +for i = 1:numel(unique_loop_var) % Prepare filtered data for this loop variable loopValue = unique_loop_var(i); @@ -98,32 +159,43 @@ for i = 1%:numel(unique_loop_var) y_bounds = [y_lower, y_upper]; % Display name (optional) - idx = find(dataTable.(loop_var) == loopValue, 1, 'first'); - dispname = equalizer_structure(dataTable.equalizer_structure(idx)); - dispname = [char(dispname),'; ',num2str(unique(dataTable.interference_path_length)),' m']; + try + idx = find(dataTable.(loop_var) == loopValue, 1, 'first'); + % dispname = char(equalizer_structure(dataTable.equalizer_structure(idx))); + dispname = [method]; + dispname = [dispname, '/ ',num2str(unique_loop_var(i)) ,' m']; + % dispname = [dispname,'; ',num2str(unique(dataTable.interference_path_length)),' m']; + dispname = [dispname, '/ PAM ', num2str(filterParams.Configurations.pam_level)]; + dispname = [dispname, '/ ', num2str(filterParams.Configurations.symbolrate.*1e-9),' GBd']; + end if plotBoundaries % Plot bounded line [hl, hp] = boundedline(x_values, y_mean, y_bounds, ... - 'alpha', 'transparency', 0.2, ... + 'alpha', 'transparency', 0.1, ... 'cmap', cols(i,:), ... 'nan', 'fill', ... 'orientation', 'vert'); - % Style the main line: thinnest, dotted, no marker + % % Style the main line: thinnest, dotted, no marker set(hl, 'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... 'Color', cols(i,:), 'DisplayName', string(dispname)); + + plt = errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 0.1, 'LineStyle', 'none', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off'); % Hide patch (shaded area) from legend set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none'); - % Add invisible scatter for DataTips - plt = scatter(x_values, y_mean, ... - 'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ... - 'HandleVisibility', 'off', 'PickableParts', 'all'); + % % Add invisible scatter for DataTips + % plt = scatter(x_values, y_mean, ... + % 'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ... + % 'HandleVisibility', 'off', 'PickableParts', 'all'); else + plt= plot(x_values,y_mean,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... - 'Color', cols(i,:), 'DisplayName', string(dispname)); + 'Color', cols(i,:), 'DisplayName', string(dispname)); + % plt= errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname)); + end % Add data tips to the invisible scatter pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)}; @@ -146,7 +218,7 @@ for i = 1%:numel(unique_loop_var) y_single = double(dataTable.(y_var)(loopFiltSingle, :)); sc = scatter(x_single, y_single, 'Marker', mkr, 'MarkerEdgeColor', cols(i, :), ... - 'LineWidth', 0.5, 'HandleVisibility', 'on', 'DisplayName', string(dispname)); + 'LineWidth', 0.5, 'HandleVisibility', 'off', 'DisplayName', string(dispname)); pair_one = {'Run ID', dataTable.run_id(loopFiltSingle, :)}; pair_two = {'Rate', dataTable.bitrate(loopFiltSingle, :) * 1e-9}; @@ -161,12 +233,14 @@ xlabel(x_var); ylabel(y_var); title([x_var, ' vs. ', y_var]); -if y_var == 'BER' +if string(y_var) == "BER" yline(4e-4, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); + yline(3.8e-3, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); + yline(2e-2, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); ylim([1e-5, 0.1]); end -xlim([15,50]); +xlim([13,35]); % Enable grid and beautify grid on; @@ -243,7 +317,6 @@ resultTable.nRows = groupCount; end - function addDatatips(sc, varargin) % addDatatips Adds custom data tip rows to a scatter plot. % @@ -283,7 +356,6 @@ end end - function cleanedTable = cleanUpTable(inputTable) % cleanUpTable Cleans a MATLAB table where numbers and NaNs are stored as strings or structs. % @@ -335,7 +407,7 @@ for i = 1:numel(varNames) else % Try convert to datetime try - cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); + cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); catch % Leave as string end @@ -391,8 +463,9 @@ for groupIdx = 1:height(groupKeys) continue; end - % Detect outliers - outlierMask = isoutlier(y_values, 'quartiles'); + % Detect outliers in log space + y_log = log10(y_values); + outlierMask = isoutlier(y_log, 'quartiles'); % or 'median', 'grubbs', etc. % If any outliers found, collect their data if any(outlierMask) diff --git a/projects/ECOC_2025/submit_dsp.m b/projects/ECOC_2025/submit_dsp.m index 019056d..183ce2b 100644 --- a/projects/ECOC_2025/submit_dsp.m +++ b/projects/ECOC_2025/submit_dsp.m @@ -12,6 +12,7 @@ arguments savePath options.parallel (1,1) logical = true options.max_occurences = 1; + options.paramstruct = struct(); end if options.parallel @@ -29,7 +30,8 @@ if options.parallel "database_name", database_name, ... 'storage_path', savePath, ... 'append_to_db', 1, ... - 'max_occurences', options.max_occurences ... + 'max_occurences', options.max_occurences, ... + 'parameters', options.paramstruct ... ); output = []; @@ -46,7 +48,8 @@ else "database_name", database_name, ... 'storage_path', savePath, ... 'append_to_db', 1, ... - 'max_occurences', options.max_occurences ... + 'max_occurences', options.max_occurences,... + 'parameters', options.paramstruct ... ); future = []; % No future since it's synchronous diff --git a/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi_database.m b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi_database.m index c34aa7d..f991d79 100644 --- a/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi_database.m +++ b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi_database.m @@ -2,17 +2,18 @@ % 1) Find RUN ID's basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -database = DBHandler("pathToDB",[basePath,'silas_labor.db']); +% database = DBHandler("pathToDB",[basePath,'silas_labor.db']); +database = DBHandler("type",'mysql'); filterParams = database.tables; filterParams.Configurations = struct( ... - 'bitrate', [], ... %[224,336,360,390,420,448] + 'bitrate', 112e9, ... %[224,336,360,390,420,448] 'db_mode', [], ... 'fiber_length', [], ... 'interference_attenuation',[], ... - 'is_mpi', 0, ... - 'pam_level', [], ... - 'rop_attenuation', 0 ... + 'interference_path_length',300, ... + 'is_mpi', 1, ... + 'pam_level', 4 ... ); selectedFields = {'Runs.run_id',... diff --git a/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_run_id.m b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_run_id.m index ebb7dfc..816505d 100644 --- a/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_run_id.m +++ b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_run_id.m @@ -124,7 +124,7 @@ for occ = 1:proc_occ % %%%%% VNLE + DFE %%%% if 0 - eq_vnle_dfe = EQ("Ne",vnle_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); + eq_vnle_dfe = EQ("Ne",vnle_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.001,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); 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); [result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,"showAnalysis",1,"postFFE",[]); From 9ce23c4a10921efc3a3c842ff18a41be08389f83 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Tue, 13 May 2025 10:24:09 +0200 Subject: [PATCH 02/30] Many changes in DBHandler new general processing structure just before splitting off the projects folder from this repo --- Classes/00_signals/Signal.m | 22 +- Classes/DataBaseHandler/DBHandler.m | 406 ++++++++++------ Classes/DataBaseHandler/Equalizerstruct.m | 68 +++ Classes/DataBaseHandler/Metricstruct.m | 87 ++++ Classes/DataBaseHandler/QueryFilter.m | 169 +++++++ Classes/DataBaseHandler/SqlFilter.m | 22 + Classes/DataBaseHandler/SqlOperator.m | 48 ++ Classes/DataBaseHandler/copyStylingFrom.m | 76 --- .../related functions/cleanUpTable.m | 32 ++ .../related functions/groupIt.m | 53 +++ .../related functions/removeGroupOutliers.m | 61 +++ Datatypes/processingMode.m | 8 + Functions/EQ_structures/dsp_runid.m | 137 ++++++ Functions/EQ_structures/duobinary_signaling.m | 177 ++++--- Functions/EQ_structures/duobinary_target.m | 94 ++-- Functions/EQ_structures/ffe.m | 163 +++++++ Functions/EQ_structures/vnle.m | 192 -------- .../EQ_structures/vnle_postfilter_mlse.m | 423 ++++++++--------- Functions/EQ_visuals/showLevelHistogram.m | 2 +- Functions/EQ_visuals/showLevelScatter.m | 66 +-- .../configureEqualizers_remove.m | 40 ++ Functions/Job_Processing/loadSignalData.m | 106 +++++ Functions/Job_Processing/preprocessSignal.m | 26 ++ Functions/Job_Processing/printResults.m | 47 ++ Functions/Job_Processing/queryRunid.m | 11 + Functions/Job_Processing/submitJobs.m | 213 +++++++++ Functions/Metrics/calc_evm.m | 42 +- Functions/Metrics/calc_std.m | 54 ++- Functions/Theory/propagation_time.m | 11 + Functions/beautifyBERplot.m | 6 +- projects/ECOC_2025/dsp_run_id.m | 29 +- .../{dsp_loop_id.m => dsp_standalone_2.m} | 2 +- projects/ECOC_2025/load_signal_standalone.m | 69 ++- .../plots_from_database/plot_mpi_trial.m | 441 ++++++++++-------- .../auswertung MPI/plot_from_database.m | 9 +- .../db_auswertung/rate_vs_ber.m | 10 +- .../db_auswertung/realization_vs_ber.m | 4 +- projects/Job_Processing/run_offline_dsp.m | 117 +++++ 38 files changed, 2440 insertions(+), 1103 deletions(-) create mode 100644 Classes/DataBaseHandler/Equalizerstruct.m create mode 100644 Classes/DataBaseHandler/Metricstruct.m create mode 100644 Classes/DataBaseHandler/QueryFilter.m create mode 100644 Classes/DataBaseHandler/SqlFilter.m create mode 100644 Classes/DataBaseHandler/SqlOperator.m delete mode 100644 Classes/DataBaseHandler/copyStylingFrom.m create mode 100644 Classes/DataBaseHandler/related functions/cleanUpTable.m create mode 100644 Classes/DataBaseHandler/related functions/groupIt.m create mode 100644 Classes/DataBaseHandler/related functions/removeGroupOutliers.m create mode 100644 Datatypes/processingMode.m create mode 100644 Functions/EQ_structures/dsp_runid.m create mode 100644 Functions/EQ_structures/ffe.m delete mode 100644 Functions/EQ_structures/vnle.m create mode 100644 Functions/Job_Processing/configureEqualizers_remove.m create mode 100644 Functions/Job_Processing/loadSignalData.m create mode 100644 Functions/Job_Processing/preprocessSignal.m create mode 100644 Functions/Job_Processing/printResults.m create mode 100644 Functions/Job_Processing/queryRunid.m create mode 100644 Functions/Job_Processing/submitJobs.m create mode 100644 Functions/Theory/propagation_time.m rename projects/ECOC_2025/{dsp_loop_id.m => dsp_standalone_2.m} (98%) create mode 100644 projects/Job_Processing/run_offline_dsp.m diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index dd4cf9e..eb41f39 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -693,6 +693,8 @@ classdef Signal return end + pkpos = sort(pkpos); + if mean(w) > 10 || mean(p) > 10 return else @@ -709,7 +711,7 @@ classdef Signal shifts = lags(pkpos); sequenceStarts = shifts; - % shifts = shifts(shifts>=0); + shifts = shifts(shifts>=0); if numel(shifts) > 0 @@ -720,17 +722,17 @@ classdef Signal for c = shifts sig = obj.delay(-c,'mode','samples'); - sig.signal = sig.signal(1:length(b)) .* -inverted; + sig.signal = sig.signal(1:length(b));% .* -inverted; S{end+1,1} = sig; end - %return/keep the sinal with the highest correlation (only within positive shifts) - [~,idx]=max(pks); - obj.signal = S{idx}.signal; - %put signal with highest corr. to first index in S array - swap = S{1}; - S{1} = S{idx}; - S{idx} = swap; + % %return/keep the sinal with the highest correlation (only within positive shifts) + % [~,idx]=max(pks); + % obj.signal = S{idx}.signal; + % %put signal with highest corr. to first index in S array + % swap = S{1}; + % S{1} = S{idx}; + % S{idx} = swap; for c = 1:numel(shifts) S{c}.logbook = []; @@ -962,7 +964,7 @@ classdef Signal % add information - if 1 + if 0 pwr_dbm = round(obj.power,3); pwr_lin = obj.power("unit",power_notation.W); diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index 6a77aec..926d016 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -83,7 +83,7 @@ classdef DBHandler < handle result = fetch(obj.conn, 'SELECT name FROM sqlite_master WHERE type="table"'); obj.tableNames = result.name; end - + catch e error('Failed to retrieve table names: %s', e.message); end @@ -169,14 +169,6 @@ classdef DBHandler < handle function healthyDB = dbIsHealthy(obj) healthyDB = false; - num_runs = obj.fetch('SELECT COUNT(*) AS total_runs FROM Runs'); - - num_configs = obj.fetch('SELECT COUNT(*) AS total_configurations FROM Configurations'); - - num_meas = obj.fetch('SELECT COUNT(*) AS total_measurements FROM Measurements'); - - assert((num_runs{1,1}==num_configs{1,1})&&(num_configs{1,1}==num_meas{1,1}),'Different num of entries per table'); - %Check for any duplicate paths duplictae_raw = obj.fetch("SELECT COALESCE(Runs.rx_raw_path,'NaN') AS rx_raw_path, COUNT(*) AS occurrences FROM Runs GROUP BY rx_raw_path HAVING COUNT(*) > 1"); duplictae_sync = obj.fetch("SELECT COALESCE(Runs.rx_sync_path,'NaN') AS rx_sync_path, COUNT(*) AS occurrences FROM Runs GROUP BY rx_sync_path HAVING COUNT(*) > 1"); @@ -190,7 +182,6 @@ classdef DBHandler < handle end - function lastID = appendToTable(obj, tableName, newRow) % appendToTable Appends a new row to the specified table % @@ -206,48 +197,52 @@ classdef DBHandler < handle error('Table %s does not exist in the database or has not been fetched.', tableName); end - % Convert newRow to a table if it is a struct + % Handle struct preprocessing before table conversion if isstruct(newRow) fields = fieldnames(newRow); - emptyFields = structfun(@isempty,newRow); + + % Handle empty fields + emptyFields = structfun(@isempty, newRow); if sum(emptyFields) > 0 - emptyFieldNames = fields(emptyFields); % use () to get a cell array + emptyFieldNames = fields(emptyFields); for idx = 1:numel(emptyFieldNames) newRow.(emptyFieldNames{idx}) = NaN; end end + + % Convert arrays to JSON strings + for i = 1:length(fields) + fieldValue = newRow.(fields{i}); + if isnumeric(fieldValue) && length(fieldValue) > 1 + newRow.(fields{i}) = jsonencode(fieldValue); + end + end + + % Convert to table newRow = struct2table(newRow); end % Ensure the new row matches the structure of the existing table existingTableStructure = obj.tables.(tableName); - % Perform data type checks and conversions + % Perform remaining data type checks and conversions for colName = newRow.Properties.VariableNames - % Extract the value and its intended column type value = newRow.(colName{1}); - existingValue = existingTableStructure.(colName{1}); - % If the value is a class object, convert it to JSON format - if isobject(value) && ~isdatetime(value) && ~isa(value,"string") + if iscell(value) && ~isempty(value) + if ischar(value{1}) || isstring(value{1}) + newRow.(colName{1}) = value{1}; + end + elseif isobject(value) && ~isdatetime(value) && ~isa(value, "string") newRow.(colName{1}) = string(jsonencode(value)); - - % If the value is a character array, convert it to a string elseif ischar(value) newRow.(colName{1}) = string(value); - - elseif isdatetime(value) - - % newRow.(colName{1}) = string(value); - end - end % Parameters for retry logic maxRetries = 50; basePause = 0.05; % seconds - attempt = 0; success = false; @@ -257,36 +252,40 @@ classdef DBHandler < handle newRow_ = obj.convertTableToCellStrings(newRow); end sqlwrite(obj.conn, tableName, newRow); - success = true; % Write successful + success = true; catch e if contains(e.message, 'database is locked') || contains(e.message, 'cannot rollback transaction') attempt = attempt + 1; - pauseTime = basePause * (1 + rand()); % Add random jitter to reduce collision chance - fprintf('Database locked. Retry %d/%d after %.3f seconds...\n', attempt, maxRetries, pauseTime); + pauseTime = basePause * (1 + rand()); + fprintf('Database locked. Retry %d/%d after %.3f seconds...\n', ... + attempt, maxRetries, pauseTime); pause(pauseTime); else + fprintf('Error details:\n'); + fprintf('Column values:\n'); + disp(newRow); error('Failed to append to the table %s: %s', tableName, e.message); end end end if ~success - error('Failed to append to table %s after %d retries due to database lock.', tableName, maxRetries); + error('Failed to append to table %s after %d retries due to database lock.', ... + tableName, maxRetries); end - % Retrieve the measurement_id of the newly inserted row for linking other tables - + % Retrieve the ID of the newly inserted row if obj.type == "mysql" - queue = "SELECT LAST_INSERT_ID()"; + query = "SELECT LAST_INSERT_ID()"; elseif obj.type == "sqlite" - queue = "SELECT last_insert_rowid()"; + query = "SELECT last_insert_rowid()"; end - result = fetch(obj.conn, queue); - lastID = result{1, 1}; % Access the value directly from the table + result = fetch(obj.conn, query); + lastID = result{1, 1}; end - function exists = checkIfRunExists(obj, table2check, column2check, value2check) + function [exists, count] = checkIfRunExists(obj, table2check, column2check, value2check) % checkIfRunExists Checks if a specific value exists in a specified column of a table % % Usage: @@ -311,7 +310,11 @@ classdef DBHandler < handle end % Construct the query to check for the value in the specified column - query = sprintf('SELECT COUNT(*) FROM %s WHERE %s = "%s"', table2check, column2check, value2check); + if isnumeric(value2check) + query = sprintf('SELECT COUNT(*) FROM %s WHERE %s = %d', table2check, column2check, value2check); + else + query = sprintf('SELECT COUNT(*) FROM %s WHERE %s = "%s"', table2check, column2check, value2check); + end % Execute the query and pass the value2check to avoid SQL injection issues try @@ -325,12 +328,21 @@ classdef DBHandler < handle exists = count > 0; if exists - disp(['The value "', value2check, '" already exists in the column "', column2check, '" of the table "', table2check, '".']); + % disp(['The value "', num2str(value2check), '" already exists in the column "', column2check, '" of the table "', table2check, '".']); else % disp(['The value "', value2check, '" does not exist in the column "', column2check, '" of the table "', table2check, '".']); end end + function hashStr = calcHash(~,object) + jsonStr = jsonencode(object); + md = java.security.MessageDigest.getInstance('MD5'); + md.update(uint8(jsonStr)); + hashBytes = typecast(md.digest, 'uint8'); + hashStr = lower(dec2hex(hashBytes)'); + hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string + end + function resultID = addProcessingResult(obj, run_id, resultData, eqParamsData) % addProcessingResult Adds a processing result and links it to an EqualizerParameters entry. @@ -348,18 +360,19 @@ classdef DBHandler < handle % resultID: The result_id of the newly inserted ProcessingResults entry. % 1. Compute hash for equalizer parameters - jsonStr = jsonencode(eqParamsData); - md = java.security.MessageDigest.getInstance('MD5'); - md.update(uint8(jsonStr)); - hashBytes = typecast(md.digest, 'uint8'); - hashStr = lower(dec2hex(hashBytes)'); - hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string + % jsonStr = jsonencode(eqParamsData); + % md = java.security.MessageDigest.getInstance('MD5'); + % md.update(uint8(jsonStr)); + % hashBytes = typecast(md.digest, 'uint8'); + % hashStr = lower(dec2hex(hashBytes)'); + % hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string + hashStr = obj.calcHash(eqParamsData); % Add hash to equalizer parameters - eqParamsData.config_hash = hashStr; + eqParamsData.hash = hashStr; % 2. Check if an equalizer configuration with the same hash exists - queryStr = sprintf('SELECT eq_id FROM EqualizerParameters WHERE config_hash = ''%s''', eqParamsData.config_hash); + queryStr = sprintf('SELECT eq_id FROM Equalizer WHERE hash = ''%s''', eqParamsData.hash); existingEntry = obj.fetch(queryStr); if ~isempty(existingEntry) @@ -367,27 +380,30 @@ classdef DBHandler < handle eq_id = existingEntry{1,1}; else % Insert the new equalizer configuration and get its eq_id - eq_id = obj.appendToTable('EqualizerParameters', eqParamsData); + eqParamsData = eqParamsData.toStruct; + eq_id = obj.appendToTable('Equalizer', eqParamsData); end % 3. Add the equalizer configuration reference and run_id to resultData - resultData.eqParam_id = eq_id; + resultData = resultData.toStruct; + resultData.eq_id = eq_id; resultData.run_id = run_id; % 4. Compute hash for the processing result tempResultData = rmfield(resultData, 'date_of_processing'); - resultJsonStr = jsonencode(tempResultData); - md2 = java.security.MessageDigest.getInstance('MD5'); % Create a new MD5 instance - md2.update(uint8(resultJsonStr)); - resultHashBytes = typecast(md2.digest, 'uint8'); - resultHashStr = lower(dec2hex(resultHashBytes)'); - resultHashStr = lower(strtrim(resultHashStr(:)')); % Convert to a lowercase string + % resultJsonStr = jsonencode(tempResultData); + % md2 = java.security.MessageDigest.getInstance('MD5'); % Create a new MD5 instance + % md2.update(uint8(resultJsonStr)); + % resultHashBytes = typecast(md2.digest, 'uint8'); + % resultHashStr = lower(dec2hex(resultHashBytes)'); + % resultHashStr = lower(strtrim(resultHashStr(:)')); % Convert to a lowercase string + resultHashStr = obj.calcHash(tempResultData); % Add the result hash to resultData - resultData.result_hash = resultHashStr; + resultData.hash = resultHashStr; % 5. Check if an identical processing result already exists - queryStr2 = sprintf('SELECT result_id FROM Results WHERE result_hash = ''%s''', resultData.result_hash); + queryStr2 = sprintf('SELECT result_id FROM Results WHERE hash = ''%s''', resultData.hash); existingResult = obj.fetch(queryStr2); if ~isempty(existingResult) @@ -397,7 +413,66 @@ classdef DBHandler < handle return; end - % 6. Insert the processing result + % 6. check if obj.tables.Results matches Metricstruct + % Fields to exclude from comparison + excludeFields = {'result_id', 'run_id', 'eq_id'}; + + % 7. Chack for new fields in Metric struct and append to + % database if necessary + ms=Metricstruct; + metricFields = setdiff(fieldnames(ms), excludeFields); + tableFields = setdiff(fieldnames(obj.tables.Results), excludeFields); + + % Check matches and find missing fields + matches = all(ismember(metricFields, tableFields)); + + if ~matches + missingFields = setdiff(metricFields, tableFields); + + + % If there are missing fields, add them to the SQL table + if ~isempty(missingFields) + for i = 1:length(missingFields) + fieldName = missingFields{i}; + + % Determine SQL data type based on MATLAB class + fieldValue = ms.(fieldName); + if isnumeric(fieldValue) + if isinteger(fieldValue) + sqlType = 'INTEGER'; + else + sqlType = 'REAL'; + end + elseif ischar(fieldValue) || isstring(fieldValue) + sqlType = 'TEXT'; + elseif isdatetime(fieldValue) + sqlType = 'DATETIME'; + elseif iscell(fieldValue) || isstruct(fieldValue) || islogical(fieldValue) + sqlType = 'TEXT'; % Store as JSON + else + sqlType = 'TEXT'; % Default to TEXT for unknown types + end + + % Create ALTER TABLE query + queryStr = sprintf('ALTER TABLE Results ADD COLUMN %s %s', fieldName, sqlType); + + try + % Execute the query + obj.fetch(queryStr); + fprintf('Added field "%s" of type %s to Results table\n', fieldName, sqlType); + catch ME + fprintf('Error adding field "%s": %s\n', fieldName, ME.message); + end + end + else + fprintf('No missing fields to add.\n'); + end + + end + + + + % 8. Insert the processing result resultID = obj.appendToTable('Results', resultData); end @@ -486,8 +561,6 @@ classdef DBHandler < handle end - - function executeSQL(obj, query) % This method executes an SQL statement using MATLAB's execute function. execute(obj.conn, query); @@ -518,22 +591,6 @@ classdef DBHandler < handle selectedFields = []; end - % Step 1: Prompt the user to input filter parameters if not provided - if isempty(filterParams) - filterParams = obj.promptFilterParameters(); - end - - % Step 2: Prompt the user to select fields to include in the SELECT statement - if isempty(selectedFields) - selectedFields = obj.promptSelectFields(); - else - if iscell(selectedFields) - - elseif isstruct(selectedFields) - - end - end - % Step 3: Construct the SQL query based on the inputs query = obj.constructSQLQuery(filterParams, selectedFields); @@ -619,16 +676,20 @@ classdef DBHandler < handle end end - function query = constructSQLQuery(obj, filterParams, selectedFields) % constructSQLQuery Constructs the SQL query based on filter parameters and selected fields. - % - % If selectedFields is provided as a struct, it is converted to a cell array. - % The conversion takes the field names and creates entries like: - % {'selectedFields.fieldName'} for each field. - % Input check for selectedFields: if it's a struct, convert it to a cell array. - if isstruct(selectedFields) + arguments + obj + filterParams + selectedFields + end + + % Step 1: Handle selectedFields conversion + if isempty(selectedFields) || (ischar(selectedFields) && strcmpi(selectedFields, 'all')) + selectedFields = obj.getTableFieldNames('Runs'); % Default to Runs table + elseif isstruct(selectedFields) + % Convert struct to cell array of 'Table.field' format newFields = {}; tableNames = fieldnames(selectedFields); for t = 1:numel(tableNames) @@ -643,39 +704,10 @@ classdef DBHandler < handle selectedFields = newFields; end - % Construct the SELECT clause dynamically based on user selection. - % (Assuming that when provided as a cell array, each entry is of the form - % 'TableName.fieldName' or, in our conversion case, 'selectedFields.fieldName'.) - selectClause = 'SELECT DISTINCT '; - for i = 1:numel(selectedFields) - fieldParts = strsplit(selectedFields{i}, '.'); - % If the field comes from the struct conversion, its first part is 'selectedFields' - % and the actual field name is in the second part. - if strcmp(fieldParts{1}, 'selectedFields') - tableName = fieldParts{1}; % not used for type checking below - fieldName = fieldParts{2}; - else - tableName = fieldParts{1}; - fieldName = fieldParts{2}; - end + % Step 2: Generate COALESCE string for SELECT clause + selectClause = obj.generateCoalesceString(selectedFields); - % Decide on COALESCE depending on the field type. - % If the table is known in obj.tables and the field is numeric, use 'NaN'. - if isfield(obj.tables, tableName) && isfield(obj.tables.(tableName), fieldName) && isnumeric(obj.tables.(tableName).(fieldName)) - selectClause = [selectClause, 'COALESCE(', selectedFields{i}, ', ''NaN'') AS ', fieldName]; - else - selectClause = [selectClause, 'COALESCE(', selectedFields{i}, ', '''') AS ', fieldName]; - end - - if i < numel(selectedFields) - selectClause = [selectClause, ', ']; - else - selectClause = [selectClause, ' ']; - end - end - - - % --- Adaptive FROM Clause --- + % Step 3: Generate FROM clause with joins mainTable = 'Runs'; fromClause = ['FROM ', mainTable, ' ']; @@ -693,58 +725,90 @@ classdef DBHandler < handle if isfield(obj.tables.(tableName), 'run_id') % Direct join to Runs - normalJoins = [normalJoins, 'LEFT JOIN ', tableName, ' ON ', mainTable, '.run_id = ', tableName, '.run_id ']; + normalJoins = [normalJoins, 'LEFT JOIN ', tableName, ... + ' ON ', mainTable, '.run_id = ', tableName, '.run_id ']; elseif isfield(obj.tables.(tableName), 'eq_id') % Equalizer depends on Results, collect this join separately - equalizerJoin = ['LEFT JOIN ', tableName, ' ON Results.eqParam_id = ', tableName, '.eq_id ']; + equalizerJoin = ['LEFT JOIN ', tableName, ... + ' ON Results.eq_id = ', tableName, '.eq_id ']; end end % Combine joins: normal joins first, Equalizer last fromClause = [fromClause, normalJoins, equalizerJoin]; + % Step 4: Generate WHERE clause if filter parameters exist + if ~isempty(filterParams) + whereClause = obj.generateWhereClause(filterParams); + if ~isempty(whereClause) + query = [selectClause, ' ', fromClause, 'WHERE ', whereClause]; + else + query = [selectClause, ' ', fromClause]; + end + else + query = [selectClause, ' ', fromClause]; + end + end - - - % --- WHERE Clause Construction --- - baseQuery = [selectClause, ' ', fromClause, 'WHERE ']; + function whereClause = generateWhereClause(obj, filterParams) filterClauses = []; - tableNames_ = fieldnames(filterParams); - for t = 1:numel(tableNames_) - tableName = tableNames_{t}; + + % If filterParams is a DbFilterParameter object, get its internal structure + if isa(filterParams, 'QueryFilter') + filterParams = filterParams.toStruct(); + end + + % Now proceed with the structure + tableNames = fieldnames(filterParams); + + for t = 1:numel(tableNames) + tableName = tableNames{t}; tableParams = filterParams.(tableName); fieldNames = fieldnames(tableParams); + for i = 1:numel(fieldNames) fieldName = fieldNames{i}; value = tableParams.(fieldName); fullName = sprintf('%s.%s', tableName, fieldName); - % Handle various types of values for SQL query construction - if isempty(value) + % Skip empty values + if isempty(value) || (isa(value, 'QueryFilter') && isempty(value.value)) continue; - elseif isnumeric(value) && isnan(value) - filterClause = sprintf('%s IS NULL', fullName); - elseif isnumeric(value) && ~isEnumeration(value) - filterClause = sprintf('%s = %f', fullName, value); - elseif islogical(value) || (isnumeric(value) && ismember(value, [0, 1])) && ~isEnumeration(value) - filterClause = sprintf('%s = %d', fullName, value); - elseif ischar(value) || isstring(value) - filterClause = sprintf('%s = ''%s''', fullName, char(value)); - elseif isEnumeration(value) - filterClause = sprintf('%s = ''%s''', fullName, value); - else - error('Unsupported data type for field "%s".', fullName); end - filterClauses = [filterClauses, filterClause, ' AND ']; + + % Handle Filter class + if isa(value, 'SqlFilter') + if isnumeric(value.value) + filterClause = sprintf('%s %s %f', ... + fullName, value.operator, value.value); + elseif ischar(value.value) || isstring(value.value) + filterClause = sprintf('%s %s ''%s''', ... + fullName, value.operator, char(value.value)); + else + continue; % Skip unsupported types + end + filterClauses = [filterClauses, filterClause, ' AND ']; + else + % Handle direct values (legacy support) + if isnumeric(value) && isnan(value) + filterClause = sprintf('%s IS NULL', fullName); + elseif isnumeric(value) + filterClause = sprintf('%s = %f', fullName, value); + elseif ischar(value) || isstring(value) + filterClause = sprintf('%s = ''%s''', fullName, char(value)); + else + continue; % Skip unsupported types + end + filterClauses = [filterClauses, filterClause, ' AND ']; + end end end - % Remove trailing ' AND ' if any filters were added. + % Remove trailing ' AND ' if any filters were added if ~isempty(filterClauses) - filterClauses = filterClauses(1:end-5); - query = [selectClause, ' ', fromClause, 'WHERE ', filterClauses]; + whereClause = filterClauses(1:end-5); else - query = [selectClause, ' ', fromClause]; + whereClause = ''; end end @@ -980,5 +1044,59 @@ classdef DBHandler < handle end + function fieldNames = getTableFieldNames(obj, tableName) + % Returns all field names for a given table as a cell array in the format {'TableName.fieldName'} + if isfield(obj.tables, tableName) + % Get raw field names + rawFields = fieldnames(obj.tables.(tableName)); + + % Create cell array with table name prefix + fieldNames = cellfun(@(x) [tableName, '.', x], ... + rawFields, ... + 'UniformOutput', false); + else + error('Table "%s" not found in obj.tables.', tableName); + end + end + + + function coalesceStr = generateCoalesceString(obj, selectedFields) + % Generates a COALESCE string for selected fields + % Input: + % selectedFields: cell array of strings in format {'Table.field'} + % e.g., {'Runs.run_id', 'Runs.bitrate'} + % Output: + % coalesceStr: string with COALESCE statements + + arguments + obj + selectedFields cell + end + + % Initialize cell array to store each COALESCE statement + coalesceStatements = cell(length(selectedFields), 1); + + % Generate COALESCE statement for each field + for i = 1:length(selectedFields) + % Split table and field name + parts = strsplit(selectedFields{i}, '.'); + if length(parts) ~= 2 + error('Field name must be in format "Table.field": %s', selectedFields{i}); + end + tableName = parts{1}; + fieldName = parts{2}; + + % Generate COALESCE statement + coalesceStatements{i} = sprintf('COALESCE(%s.%s, ''NaN'') AS %s ', ... + tableName, fieldName, fieldName); + end + + % Join with comma, newline and MATLAB string continuation + coalesceStr = strjoin(coalesceStatements, [', ' sprintf('\n ')]); + % Add initial newline and spacing for formatting + coalesceStr = [sprintf('SELECT DISTINCT \n ') coalesceStr]; + end + + end end diff --git a/Classes/DataBaseHandler/Equalizerstruct.m b/Classes/DataBaseHandler/Equalizerstruct.m new file mode 100644 index 0000000..3a72307 --- /dev/null +++ b/Classes/DataBaseHandler/Equalizerstruct.m @@ -0,0 +1,68 @@ +classdef Equalizerstruct + % Equalizerstruct - Class to store and manage equalizer structure data + + properties + eq_id (1,1) double {mustBeNumeric} = NaN + equalizer_structure equalizer_structure = equalizer_structure.ffe + eq + mlse + comment char = string.empty() % Changed to string with proper empty initialization + hash char = string.empty() % Changed to string with proper empty initialization + end + + methods + function obj = Equalizerstruct(varargin) + % Constructor method for Equalizerstruct + % Can be called empty or with name-value pairs + + if nargin > 0 + for i = 1:2:nargin + if isprop(obj, varargin{i}) + obj.(varargin{i}) = varargin{i+1}; + else + error('Property %s does not exist in Equalizerstruct', varargin{i}); + end + end + end + end + + function s = toStruct(obj) + % Convert the object to a struct + s = struct(); + props = properties(obj); + for i = 1:length(props) + s.(props{i}) = obj.(props{i}); + end + end + + function str = toString(obj) + % Convert the object to a formatted string + s = obj.toStruct(); + str = sprintf('Equalizerstruct:\n'); + fields = fieldnames(s); + for i = 1:length(fields) + val = s.(fields{i}); + if isempty(val) + str = sprintf('%s%s: []\n', str, fields{i}); + elseif isnumeric(val) && length(val) > 1 + str = sprintf('%s%s: [%s]\n', str, fields{i}, num2str(val')); + else + str = sprintf('%s%s: %s\n', str, fields{i}, string(val)); + end + end + end + end + + methods (Static) + function obj = fromStruct(s) + % Create an Equalizerstruct object from a struct + obj = Equalizerstruct(); + fields = fieldnames(s); + for i = 1:length(fields) + if isprop(obj, fields{i}) + obj.(fields{i}) = s.(fields{i}); + end + end + end + end +end \ No newline at end of file diff --git a/Classes/DataBaseHandler/Metricstruct.m b/Classes/DataBaseHandler/Metricstruct.m new file mode 100644 index 0000000..9161e47 --- /dev/null +++ b/Classes/DataBaseHandler/Metricstruct.m @@ -0,0 +1,87 @@ +classdef Metricstruct + % ResultData - Class to store and manage metric data from signal processing results + + properties + result_id (1,1) double {mustBeNumeric} = NaN + run_id (1,1) double {mustBeNumeric} = NaN + eqParam_id (1,1) double {mustBeNumeric} = NaN + date_of_processing (1,1) datetime = datetime('now') + + numBits (1,1) double {mustBeInteger, mustBeNonnegative} = 0 + BER (1,1) double {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(BER,1)} = 0 + numBitErr (1,1) double {mustBeInteger, mustBeNonnegative} = 0 + BER_precoded (1,1) double {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(BER_precoded,1)} = 0 + numBitErr_precoded (1,1) double {mustBeInteger, mustBeNonnegative} = 0 + + SNR (1,1) double {mustBeNumeric} = NaN + SNR_level (:,1) double {mustBeNumeric} = [] + STD (1,1) double {mustBeNumeric} = NaN + STD_level (:,1) double {mustBeNumeric, mustBeNonnegative} = [] + STDrx (1,1) double {mustBeNumeric} = NaN + STDrx_level (:,1) double {mustBeNumeric, mustBeNonnegative} = [] + EVM (1,1) double {mustBeNumeric} = NaN + EVM_level (:,1) double {mustBeNumeric} = [] + + GMI (1,1) double {mustBeNumeric} = NaN + AIR (1,1) double {mustBeNumeric} = NaN + Alpha (:,1) double {mustBeNumeric} = [] + MLSE_dir (:,1) double {mustBeNumeric} = [] + end + + methods + function obj = Metricstruct(varargin) + % Constructor method for ResultData + % Can be called empty or with name-value pairs + + % Process name-value pairs if provided + if nargin > 0 + for i = 1:2:nargin + if isprop(obj, varargin{i}) + obj.(varargin{i}) = varargin{i+1}; + else + error('Property %s does not exist in ResultData', varargin{i}); + end + end + end + end + + function s = toStruct(obj) + % Convert the object to a struct + s = struct(); + props = properties(obj); + for i = 1:length(props) + s.(props{i}) = obj.(props{i}); + end + end + + function str = toString(obj) + % Convert the object to a formatted string + s = obj.toStruct(); + str = sprintf('ResultData:\n'); + fields = fieldnames(s); + for i = 1:length(fields) + val = s.(fields{i}); + if isempty(val) + str = sprintf('%s%s: []\n', str, fields{i}); + elseif isnumeric(val) && length(val) > 1 + str = sprintf('%s%s: [%s]\n', str, fields{i}, num2str(val')); + else + str = sprintf('%s%s: %s\n', str, fields{i}, string(val)); + end + end + end + end + + methods (Static) + function obj = fromStruct(s) + % Create a ResultData object from a struct + obj = Metricstruct(); + fields = fieldnames(s); + for i = 1:length(fields) + if isprop(obj, fields{i}) + obj.(fields{i}) = s.(fields{i}); + end + end + end + end +end \ No newline at end of file diff --git a/Classes/DataBaseHandler/QueryFilter.m b/Classes/DataBaseHandler/QueryFilter.m new file mode 100644 index 0000000..86dfe01 --- /dev/null +++ b/Classes/DataBaseHandler/QueryFilter.m @@ -0,0 +1,169 @@ +% QueryFilter - Class for building SQL WHERE conditions for database queries. +% +% Usage: +% qf = QueryFilter(); +% qf.where('Runs', 'fiber_length', SqlOperator.GREATER_THAN, 5); +% qf.where('Runs', 'bitrate', SqlOperator.IN, [224, 336, 448]); +% qf.where('Runs', 'is_mpi', SqlOperator.EQUALS, 0); +% +% % Convert to struct for use in DBHandler or other query functions: +% filterStruct = qf.toStruct(); +% +% % Display current filters: +% disp(qf); + +classdef QueryFilter < handle + + + properties (Access = private) + filters struct = struct() + end + + methods + function obj = QueryFilter(oldFormat) + % Constructor - optionally convert from old filter format + if nargin > 0 && isstruct(oldFormat) + tableNames = fieldnames(oldFormat); + for t = 1:length(tableNames) + tableName = tableNames{t}; + if isstruct(oldFormat.(tableName)) + fields = fieldnames(oldFormat.(tableName)); + for f = 1:length(fields) + fieldName = fields{f}; + value = oldFormat.(tableName).(fieldName); + if ~isempty(value) + obj.where(tableName, fieldName, value); + end + end + end + end + end + end + + function where(obj, tableName, fieldName, operator, value) + % Add a WHERE condition to the query + % + % Inputs: + % tableName - Name of the database table (char) + % fieldName - Name of the field/column (char) + % operator - SqlOperator enum or value if using EQUALS + % value - Value to filter by + % + % Example: + % filter.where('Runs', 'fiber_length', SqlOperator.GREATER_THAN, 5) + arguments + obj + tableName char + fieldName char + operator SqlOperator + value = [] + end + + % Handle case where operator is the value (using default EQUALS) + if nargin < 5 + value = operator; + operator = SqlOperator.EQUALS; + end + + % Validate operator type + if ~isa(operator, 'SqlOperator') + error('Operator must be a SqlOperator enumeration'); + end + + % Validate value based on operator + obj.validateValue(operator, value); + + % Create filter + filter = SqlFilter(value, operator.toSqlString()); + + % Add to filters + if ~isfield(obj.filters, tableName) + obj.filters.(tableName) = struct(); + end + obj.filters.(tableName).(fieldName) = filter; + end + + function s = toStruct(obj) + % Convert filters to struct format for database query + s = obj.filters; + end + + function display(obj) + % Custom display of filter conditions + fprintf('QueryFilter with conditions:\n'); + if isempty(fieldnames(obj.filters)) + fprintf(' No filters set\n'); + return; + end + + tables = fieldnames(obj.filters); + for t = 1:length(tables) + tableName = tables{t}; + if ~isempty(fieldnames(obj.filters.(tableName))) + fprintf('\nTable: %s\n', tableName); + fields = fieldnames(obj.filters.(tableName)); + for f = 1:length(fields) + fieldName = fields{f}; + filter = obj.filters.(tableName).(fieldName); + if isnumeric(filter.value) + if length(filter.value) > 1 + valueStr = ['[', num2str(filter.value), ']']; + else + valueStr = num2str(filter.value); + end + elseif isempty(filter.value) + valueStr = 'empty'; + else + valueStr = char(filter.value); + end + fprintf(' %s %s %s\n', fieldName, filter.operator, valueStr); + end + end + end + end + + function clear(obj, tableName) + % Clear all filters or filters for a specific table + if nargin < 2 + obj.filters = struct(); + else + if isfield(obj.filters, tableName) + obj.filters = rmfield(obj.filters, tableName); + end + end + end + + function remove(obj, tableName, fieldName) + % Remove a specific filter + if isfield(obj.filters, tableName) && ... + isfield(obj.filters.(tableName), fieldName) + obj.filters.(tableName) = rmfield(obj.filters.(tableName), fieldName); + end + end + end + + methods (Access = private) + function validateValue(~, operator, value) + % Validate value based on operator type + switch operator + case SqlOperator.BETWEEN + if ~isnumeric(value) || length(value) ~= 2 + error('BETWEEN operator requires array of 2 numbers'); + end + case SqlOperator.IN + if ~isnumeric(value) || isempty(value) + error('IN operator requires non-empty array'); + end + case SqlOperator.LIKE + if ~ischar(value) && ~isstring(value) + error('LIKE operator requires string value'); + end + otherwise + % For other operators, just ensure value is not empty + if isempty(value) + error('Value cannot be empty'); + end + end + end + end +end \ No newline at end of file diff --git a/Classes/DataBaseHandler/SqlFilter.m b/Classes/DataBaseHandler/SqlFilter.m new file mode 100644 index 0000000..3dbc07f --- /dev/null +++ b/Classes/DataBaseHandler/SqlFilter.m @@ -0,0 +1,22 @@ +% SqlFilter - Internal class for holding a value and SQL operator for a filter condition. +% +% Usage: +% f = SqlFilter(10, SqlOperator.GREATER_THAN.toSqlString()); +% % f.value == 10, f.operator == '>' + + +classdef SqlFilter + properties + value + operator char = '=' + end + + methods + function obj = SqlFilter(value, operator) + obj.value = value; + if nargin > 1 + obj.operator = operator; + end + end + end +end \ No newline at end of file diff --git a/Classes/DataBaseHandler/SqlOperator.m b/Classes/DataBaseHandler/SqlOperator.m new file mode 100644 index 0000000..4e4beea --- /dev/null +++ b/Classes/DataBaseHandler/SqlOperator.m @@ -0,0 +1,48 @@ +% SqlOperator - Enumeration of supported SQL operators for query building. +% +% Usage: +% op = SqlOperator.GREATER_THAN; +% opStr = op.toSqlString(); % returns '>' +% +% Supported operators: +% EQUALS, GREATER_THAN, LESS_THAN, GREATER_EQUAL, LESS_EQUAL, +% NOT_EQUAL, IN, BETWEEN, LIKE + +classdef SqlOperator < uint32 + enumeration + EQUALS (1) % == + GREATER_THAN (2) % > + LESS_THAN (3) % < + GREATER_EQUAL (4) % >= + LESS_EQUAL (5) % <= + NOT_EQUAL (6) % != + IN (7) % IN + BETWEEN (8) % BETWEEN + LIKE (9) % LIKE + end + + methods + function op = toSqlString(obj) + switch obj + case SqlOperator.EQUALS + op = '='; + case SqlOperator.GREATER_THAN + op = '>'; + case SqlOperator.LESS_THAN + op = '<'; + case SqlOperator.GREATER_EQUAL + op = '>='; + case SqlOperator.LESS_EQUAL + op = '<='; + case SqlOperator.NOT_EQUAL + op = '!='; + case SqlOperator.IN + op = 'IN'; + case SqlOperator.BETWEEN + op = 'BETWEEN'; + case SqlOperator.LIKE + op = 'LIKE'; + end + end + end +end \ No newline at end of file diff --git a/Classes/DataBaseHandler/copyStylingFrom.m b/Classes/DataBaseHandler/copyStylingFrom.m deleted file mode 100644 index aaf110f..0000000 --- a/Classes/DataBaseHandler/copyStylingFrom.m +++ /dev/null @@ -1,76 +0,0 @@ -function copyStylingFrom(figNumSource, figNumTgt) - % Get handles to the source and target figures - sourceFig = figure(figNumSource); - targetFig = figure(figNumTgt); - - % Get axes of source and target figures - sourceAxes = findall(sourceFig, 'type', 'axes'); - targetAxes = findall(targetFig, 'type', 'axes'); - - % Ensure the number of axes match - if length(sourceAxes) ~= length(targetAxes) - error('Number of axes in source and target figures must be the same.'); - end - - % Loop through each pair of axes and copy styling properties - for i = 1:length(sourceAxes) - copyAxesProperties(sourceAxes(i), targetAxes(i)); - end - - % Apply general figure properties if desired - targetFig.Color = sourceFig.Color; % Background color -end - -function copyAxesProperties(sourceAx, targetAx) - % List of properties to copy from source to target axes - propsToCopy = {'XColor', 'YColor', 'ZColor', 'FontSize', 'FontName', ... - 'GridColor', 'GridLineStyle', 'MinorGridColor', 'Box', ... - 'XGrid', 'YGrid', 'ZGrid', 'XMinorGrid', 'YMinorGrid', 'ZMinorGrid', ... - 'LineWidth', 'TitleFontSizeMultiplier', 'LabelFontSizeMultiplier'}; - - % Copy properties from source to target - for i = 1:length(propsToCopy) - try - targetAx.(propsToCopy{i}) = sourceAx.(propsToCopy{i}); - catch - % Skip property if it doesn't exist or can't be copied - end - end - - % Copy axis labels and titles - targetAx.Title.String = sourceAx.Title.String; - targetAx.XLabel.String = sourceAx.XLabel.String; - targetAx.YLabel.String = sourceAx.YLabel.String; - targetAx.ZLabel.String = sourceAx.ZLabel.String; - - % Copy children elements like lines, patches, etc. - sourceChildren = allchild(sourceAx); - targetChildren = allchild(targetAx); - - % Ensure the number of children elements match - if length(sourceChildren) ~= length(targetChildren) - warning('Number of elements in source and target axes differ. Styling may not be applied completely.'); - end - - % Copy properties of children (like lines, patches, etc.), except colors and legends - for i = 1:min(length(sourceChildren), length(targetChildren)) - copyObjectProperties(sourceChildren(i), targetChildren(i)); - end -end - -function copyObjectProperties(sourceObj, targetObj) - % List of common properties to copy for plot elements (lines, patches, etc.) - propsToCopy = {'LineStyle', 'LineWidth', 'Marker', 'MarkerSize', ... - 'MarkerEdgeColor', 'MarkerFaceColor', 'DisplayName'}; - - % Copy properties from source to target, excluding colors - for i = 1:length(propsToCopy) - try - if ~contains(propsToCopy{i}, 'Color') % Skip color properties - targetObj.(propsToCopy{i}) = sourceObj.(propsToCopy{i}); - end - catch - % Skip property if it doesn't exist or can't be copied - end - end -end diff --git a/Classes/DataBaseHandler/related functions/cleanUpTable.m b/Classes/DataBaseHandler/related functions/cleanUpTable.m new file mode 100644 index 0000000..239ca30 --- /dev/null +++ b/Classes/DataBaseHandler/related functions/cleanUpTable.m @@ -0,0 +1,32 @@ +function cleanedTable = cleanUpTable(inputTable) +% Converts string numbers to numeric, 'NaN' to NaN, and tries to convert date strings to datetime. + +cleanedTable = inputTable; +varNames = cleanedTable.Properties.VariableNames; + +for i = 1:numel(varNames) + col = cleanedTable.(varNames{i}); + if iscell(col) + numericCol = str2double(col); + if all(isnan(numericCol) == strcmpi(col, 'NaN') | cellfun(@isempty, col)) + cleanedTable.(varNames{i}) = numericCol; + else + try + cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); + catch + cleanedTable.(varNames{i}) = string(col); + end + end + elseif isstring(col) + numericCol = str2double(col); + if all(isnan(numericCol) == strcmpi(col, "NaN")) + cleanedTable.(varNames{i}) = numericCol; + else + try + cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); + catch + end + end + end +end +end \ No newline at end of file diff --git a/Classes/DataBaseHandler/related functions/groupIt.m b/Classes/DataBaseHandler/related functions/groupIt.m new file mode 100644 index 0000000..85a1851 --- /dev/null +++ b/Classes/DataBaseHandler/related functions/groupIt.m @@ -0,0 +1,53 @@ +function resultTable = groupIt(fixedVars, dataTable, aggregationFunction) +% groupIt Groups data in a table based on fixedVars and applies aggregationFunction to numeric data. +% +% resultTable = groupIt(fixedVars, dataTable, aggregationFunction) +% +% Inputs: +% fixedVars - Cell array of variable names to group by +% dataTable - Input MATLAB table +% aggregationFunction - Function handle (e.g., @mean, @min, @max) +% +% Output: +% resultTable - Grouped and aggregated table + +[G, groupKeys] = findgroups(dataTable(:, fixedVars)); +varNames = dataTable.Properties.VariableNames; +nVars = numel(varNames); +aggData = cell(height(groupKeys), nVars); +groupCount = zeros(height(groupKeys), 1); + +for i = 1:height(groupKeys) + idx = (G == i); + groupCount(i) = sum(idx); + for j = 1:nVars + colData = dataTable.(varNames{j}); + if isnumeric(colData) + if any(idx) + aggData{i, j} = aggregationFunction(colData(idx)); + else + aggData{i, j} = NaN; + end + else + if iscell(colData) + nonEmptyIdx = find(idx & ~cellfun(@isempty, colData), 1); + if ~isempty(nonEmptyIdx) + aggData{i, j} = colData{nonEmptyIdx}; + else + aggData{i, j} = []; + end + else + nonEmptyIdx = find(idx, 1); + if ~isempty(nonEmptyIdx) + aggData{i, j} = colData(nonEmptyIdx); + else + aggData{i, j} = []; + end + end + end + end +end + +resultTable = cell2table(aggData, 'VariableNames', varNames); +resultTable.nRows = groupCount; +end \ No newline at end of file diff --git a/Classes/DataBaseHandler/related functions/removeGroupOutliers.m b/Classes/DataBaseHandler/related functions/removeGroupOutliers.m new file mode 100644 index 0000000..cd287da --- /dev/null +++ b/Classes/DataBaseHandler/related functions/removeGroupOutliers.m @@ -0,0 +1,61 @@ +function [cleanedTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var) +% removeGroupOutliers removes outliers in y_var within each group defined by fixedVars. +% +% [cleanedTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var) +% +% Inputs: +% dataTable - Input MATLAB table +% fixedVars - Cell array of variable names to group by +% y_var - Name of the variable to check for outliers (string or char) +% +% Outputs: +% cleanedTable - Table with outliers removed +% outliersTable - Table of removed outlier rows + +[G, groupKeys] = findgroups(dataTable(:, fixedVars)); +keepIdx = true(height(dataTable), 1); +outlierRecords = []; + +for groupIdx = 1:height(groupKeys) + groupRows = (G == groupIdx); + y_values = dataTable.(y_var)(groupRows); + + % Skip groups with fewer than 3 points + if sum(groupRows) < 3 + continue; + end + + % Detect outliers in log10 space (robust for BER, etc.) + y_log = log10(y_values); + outlierMask = isoutlier(y_log, 'quartiles', 1); + + if any(outlierMask) + groupData = dataTable(groupRows, :); + outlierGroupTable = groupData(outlierMask, :); + + % Optionally, add group key values for traceability + for k = 1:numel(fixedVars) + outlierGroupTable.(['Group_', fixedVars{k}]) = repmat(groupKeys{groupIdx, k}, height(outlierGroupTable), 1); + end + + outlierRecords = [outlierRecords; outlierGroupTable]; %#ok + end + + % Mark outliers for removal + groupRowIdx = find(groupRows); + keepIdx(groupRowIdx(outlierMask)) = false; +end + +cleanedTable = dataTable(keepIdx, :); + +if isempty(outlierRecords) + outliersTable = table(); +else + outliersTable = outlierRecords; +end + +nRemoved = sum(~keepIdx); +nTotalOriginal = height(dataTable); +fprintf('Removed %d outliers from the data table (%.2f%% of total %d entries).\n', ... + nRemoved, 100*nRemoved/nTotalOriginal, nTotalOriginal); +end \ No newline at end of file diff --git a/Datatypes/processingMode.m b/Datatypes/processingMode.m new file mode 100644 index 0000000..c47dc7a --- /dev/null +++ b/Datatypes/processingMode.m @@ -0,0 +1,8 @@ + +classdef processingMode < int32 + enumeration + serial (1) + parallel (2) + end + +end \ No newline at end of file diff --git a/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m new file mode 100644 index 0000000..c3d6a63 --- /dev/null +++ b/Functions/EQ_structures/dsp_runid.m @@ -0,0 +1,137 @@ +function [output] = dsp_runid(run_id, options) + +arguments + run_id + options.append_to_db = 0; + options.max_occurences = 4; + options.parameters = struct(); + options.database_path + options.database_name + options.storage_path +end + +% Initialize output structures +output.ffe_package = {}; +output.mlse_package = {}; +output.vnle_package = {}; +output.dbtgt_package = {}; +output.dbenc_package = {}; + +% Initialize database connection +database = DBHandler("pathToDB", [options.database_path, options.database_name], "type", "sqlite"); +dataTable = queryRunid(run_id, database); +fsym = dataTable.symbolrate; +M = double(dataTable.pam_level); +duob_mode = db_mode(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 + + +% Handle Settings and argument replacement + +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.00; + +use_ffe = 1; +use_vnle_mlse = 1; +use_dbtgt = 1; +use_dbenc = 0; + +addProcessingResultToDatabase = 0; + +% Overwrite default parameters if given in options.parameters +paramStruct = options.parameters; +if ~isempty(paramStruct) + paramNames = fieldnames(paramStruct); + for i = 1:numel(paramNames) + thisName = paramNames{i}; + thisValue = paramStruct.(thisName); + eval([thisName ' = thisValue;']); + end +end + +% Configure equalizers +eq_lin = EQ("Ne",[50,0,0],"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",1); +pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); +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); +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); +% Duobinary signaling (db encoded) +mlse_db_enc = MLSE_viterbi("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, ... + "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); + + +% Load signal data +[Tx_bits, Symbols, Scpe_cell, ~] = loadSignalData(dataTable, options); + + +for r = 1:numel(Scpe_cell) + + % Preprocess signal + Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym); + + if duob_mode ~= db_mode.db_encoded + if use_ffe + ffe_results = ffe(eq_lin,M,Scpe_sig,Symbols,Tx_bits,... + "precode_mode",duob_mode,... + 'showAnalysis',1,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + output.ffe_package{r} = ffe_results; + if options.append_to_db + database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config); + end + end + + if use_vnle_mlse + [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode,... + 'showAnalysis', 1, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + 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_dbtgt + dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, ... + 'showAnalysis', 0,... + "postFFE", []); + output.dbtgt_package{r} = dbt_results; + if options.append_to_db + database.addProcessingResult(run_id, dbt_results.metrics, dbt_results.config); + end + end + end + + if duob_mode == db_mode.db_encoded + 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 + database.addProcessingResult(run_id, db_results.metrics, db_results.config); + end + end + + +end + + +end \ No newline at end of file diff --git a/Functions/EQ_structures/duobinary_signaling.m b/Functions/EQ_structures/duobinary_signaling.m index 69c0449..013e184 100644 --- a/Functions/EQ_structures/duobinary_signaling.m +++ b/Functions/EQ_structures/duobinary_signaling.m @@ -1,96 +1,129 @@ -function [eq_package] = duobinary_signaling(eq_, mlse_,M ,rx_signal, tx_symbols, tx_bits,options) -%Duobinary Signaling +function [db_results] = duobinary_signaling(eq_, mlse_, M, rx_signal, tx_symbols, tx_bits, options) +% DUOBINARY_SIGNALING Processes signals through duobinary signaling +% +% Inputs: +% eq_ - Equalizer object +% mlse_ - MLSE object +% M - Modulation order +% rx_signal - Received signal +% tx_symbols - Transmitted symbols +% tx_bits - Transmitted bits +% options - Optional parameters +% +% Outputs: +% db_results - Results from duobinary signaling processing + arguments - eq_ - mlse_ - M - rx_signal - tx_symbols - tx_bits - options.postFFE = []; + eq_ + mlse_ + M + rx_signal + tx_symbols + tx_bits + options.precode_mode db_mode + options.showAnalysis = 0 + options.eth_style_symbol_mapping = 0 + options.postFFE = [] + options.database = [] end +%% Process signals through equalizer +[eq_signal, eq_noise] = eq_.process(rx_signal, tx_symbols); -[eq_signal, eq_noise] = eq_.process(rx_signal,tx_symbols); - +% Apply post-FFE if provided if ~isempty(options.postFFE) - [eq_signal,eq_noise] = options.postFFE.process(eq_signal,tx_symbols); + [eq_signal, eq_noise] = options.postFFE.process(eq_signal, tx_symbols); end - +% Process through MLSE eq_signal = mlse_.process(eq_signal); +% Apply duobinary encoding and decoding eq_signal = Duobinary().encode(eq_signal); eq_signal = Duobinary().decode(eq_signal); -% M = numel(unique(eq_signal.signal)); -rx_bits = PAMmapper(M,0).demap(eq_signal); +% Demap symbols to bits +rx_bits = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).demap(eq_signal); -[bits_db,errors_db,ber_db,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); +%% Calculate BER and metrics +[bits_db, errors_db, ber_db, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); -eq_package.ber = ber_db; - -resultsDBsignaling = struct( ... - 'result_id', NaN, ... % - 'run_id', NaN, ... % Beispielhafte Run-ID - 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle - 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit - 'numBits', bits_db, ... % Beispiel: 1.000.000 Bits - 'numBitErr', errors_db, ... % Beispiel: 120 Bitfehler - 'BER_precoded', [], ... % BER = 120 / 1.000.000 - 'numBitErr_precoded', [], ... % Beispiel: 120 Bitfehler - 'BER', ber_db, ... % BER = 120 / 1.000.000 - 'SNR', [], ... % Beispielhafte SNR - 'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array - 'GMI', [], ... % Beispielhafter GMI-Wert - 'AIR', [], ... % Beispielhafter AIR-Wert - 'EVM', [], ... % Beispielhafte EVM - 'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array - 'Alpha', [] ... % Beispielhafter Alpha-Wert - ); +% Calculate performance metrics +[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); +[gmi] = calc_air(eq_signal, tx_symbols, "skip_front", 10000, "skip_end", 10000); +air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M)); +[evm_total, evm_lvl] = calc_evm(eq_signal, tx_symbols); +[std_total, std_lvl] = calc_std(eq_signal, tx_symbols); +[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols); +%% Prepare output structure +% Determine postFFE order if ~isempty(options.postFFE) npostFFE = options.postFFE.order; else npostFFE = 0; end -equalizerConfigDBsignaling = struct( ... - 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt - 'equalizer_structure', int32(equalizer_structure.db_encoded), ... % Beispiel: 1 (z.B. für vnle) - 'M', M, ... % Ordnung der PAM-Konstellation - 'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String - 'db_target', 1, ... % 0 oder 1 - 'diff_precode', 1, ... % 0 oder 1 - 'postFFE', ~isempty(options.postFFE), ... % Beispielwert - 'NpostFFE', npostFFE, ... % Beispielwert - 'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung - 'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung - 'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung - 'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung - 'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung - 'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung - 'K', eq_.K, ... % Samples pro Symbol - 'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap - 'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1) - 'training_length', eq_.training_length, ... % Anzahl Trainingssymbole - 'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe - 'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung) - 'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung) - 'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung) - 'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD - 'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus - 'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung) - 'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung) - 'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung) - 'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD - 'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String - 'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert - 'comment', 'function: duobinary_target.m', ... % Zusätzliche Kommentare - 'config_hash', NaN ... - ); +% Create results structure +db_results = struct(); +db_results.metrics = Metricstruct; +db_results.metrics.result_id = NaN; +db_results.metrics.run_id = NaN; +db_results.metrics.eqParam_id = NaN; +db_results.metrics.date_of_processing = datetime('now'); +db_results.metrics.BER = ber_db; +db_results.metrics.numBits = bits_db; +db_results.metrics.numBitErr = errors_db; +db_results.metrics.SNR = snr; +db_results.metrics.SNR_level = snr_lvl; +db_results.metrics.STD = std_total; +db_results.metrics.STD_level = std_lvl; +db_results.metrics.STDrx = std_rxraw_total; +db_results.metrics.STDrx_level = std_rxraw_lvl; +db_results.metrics.GMI = gmi; +db_results.metrics.AIR = air; +db_results.metrics.EVM = evm_total; +db_results.metrics.EVM_level = evm_lvl; +db_results.metrics.MLSE_dir = mlse_.DIR; -eq_package.resultsDBsignaling = resultsDBsignaling; -eq_package.equalizerConfigDBsignaling = equalizerConfigDBsignaling; +% Create configuration structure +eq_.e = []; +eq_.e2 = []; +eq_.e3 = []; +db_results.config = Equalizerstruct(); +db_results.config.eq = jsonencode(eq_); +mlse_.DIR = []; +db_results.config.mlse = jsonencode(mlse_); +db_results.config.equalizer_structure = int32(equalizer_structure.db_encoded); +db_results.config.comment = 'function: duobinary_signaling'; +%% Display analysis if requested +if options.showAnalysis + displayAnalysis(eq_noise, eq_signal, rx_signal, eq_, tx_symbols, M, options.postFFE); +end + +end + +%% Helper Function +function displayAnalysis(eq_noise, eq_signal, rx_signal, eq_, tx_symbols, M, postFFE) +% Display analysis plots and metrics +figure(336); +showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after Duobinary'); + +if ~isempty(postFFE) + showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338); +end + +showEQfilter(eq_.e, eq_signal.fs.*2); + +figure(341); clf; +showLevelHistogram(eq_signal, tx_symbols, "fignum", 341); + +warning off +figure(400); clf; +showLevelScatter(eq_signal, tx_symbols, "fignum", 400); + +figure(401); clf; +showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401); +warning on end \ No newline at end of file diff --git a/Functions/EQ_structures/duobinary_target.m b/Functions/EQ_structures/duobinary_target.m index 8d66efb..b5ff07c 100644 --- a/Functions/EQ_structures/duobinary_target.m +++ b/Functions/EQ_structures/duobinary_target.m @@ -1,4 +1,4 @@ -function [eq_package] = duobinary_target(eq_, mlse_,M, rx_signal, tx_symbols, tx_bits, options) +function [db_results] = duobinary_target(eq_, mlse_,M, rx_signal, tx_symbols, tx_bits, options) arguments eq_ @@ -22,7 +22,7 @@ if ~isempty(options.postFFE) [eq_signal,eq_noise] = options.postFFE.process(eq_signal,db_ref_sequence); end -% dir = [1,1]; +mlse_.DIR = [1,1]; mlse_sig_sd = mlse_.process(eq_signal); mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd); @@ -73,70 +73,36 @@ end rx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); [bits_db,errors_db,ber_db,errorIndice_db] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); -eq_package.ber = ber_db; -resultsDBtgt = struct( ... - 'result_id', NaN, ... % - 'run_id', NaN, ... % Beispielhafte Run-ID - 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle - 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit - 'numBits', bits_db, ... % Beispiel: 1.000.000 Bits - 'numBitErr', errors_db, ... % Beispiel: 120 Bitfehler - 'BER_precoded', ber_db_diff_precoded, ... % BER = 120 / 1.000.000 - 'numBitErr_precoded', errors_db_diff_precoded, ... % Beispiel: 120 Bitfehler - 'BER', ber_db, ... % BER = 120 / 1.000.000 - 'SNR', [], ... % Beispielhafte SNR - 'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array - 'GMI', [], ... % Beispielhafter GMI-Wert - 'AIR', [], ... % Beispielhafter AIR-Wert - 'EVM', [], ... % Beispielhafte EVM - 'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array - 'Alpha', [] ... % Beispielhafter Alpha-Wert - ); +db_results = struct(); +db_results.metrics = Metricstruct; +db_results.metrics.result_id = NaN; +db_results.metrics.run_id = NaN; +db_results.metrics.eqParam_id = NaN; +db_results.metrics.date_of_processing = datetime('now'); +db_results.metrics.BER = ber_db; +db_results.metrics.numBits = bits_db; +db_results.metrics.numBitErr = errors_db; +db_results.metrics.BER_precoded = ber_db_diff_precoded; +db_results.metrics.numBitErr_precoded = errors_db_diff_precoded; +db_results.metrics.SNR = NaN; +db_results.metrics.SNR_level = NaN; +db_results.metrics.GMI = NaN; +db_results.metrics.AIR = NaN; +db_results.metrics.EVM = NaN; +db_results.metrics.EVM_level = NaN; +db_results.metrics.MLSE_dir = mlse_.DIR; -if ~isempty(options.postFFE) - npostFFE = options.postFFE.order; -else - npostFFE = 0; -end - -equalizerConfigDBtgt = struct( ... - 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt - 'equalizer_structure', int32(equalizer_structure.vnle_db_mlse), ... % Beispiel: 1 (z.B. für vnle) - 'M', M, ... % Ordnung der PAM-Konstellation - 'target_constellation', jsonencode(round(db_ref_constellation,5)), ... % Beispielhafter Target-String - 'db_target', 1, ... % 0 oder 1 - 'diff_precode', int32(options.precode_mode), ... % 0 oder 1 - 'postFFE', ~isempty(options.postFFE), ... % Beispielwert - 'NpostFFE', npostFFE, ... % Beispielwert - 'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung - 'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung - 'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung - 'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung - 'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung - 'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung - 'K', eq_.K, ... % Samples pro Symbol - 'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap - 'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1) - 'training_length', eq_.training_length, ... % Anzahl Trainingssymbole - 'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe - 'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung) - 'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung) - 'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung) - 'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD - 'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus - 'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung) - 'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung) - 'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung) - 'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD - 'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String - 'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert - 'comment', 'function: duobinary_target.m', ... % Zusätzliche Kommentare - 'config_hash', NaN ... - ); - -eq_package.resultsDBtgt = resultsDBtgt; -eq_package.equalizerConfigDBtgt = equalizerConfigDBtgt; +% Create DB results structure +db_results.config = Equalizerstruct(); +eq_.e = []; +eq_.e2 = []; +eq_.e3 = []; +db_results.config.eq = jsonencode(eq_); +mlse_.DIR = []; +db_results.config.mlse = jsonencode(mlse_); +db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse); +db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)'; if options.showAnalysis eq_noise = eq_noise - mean(eq_noise.signal); diff --git a/Functions/EQ_structures/ffe.m b/Functions/EQ_structures/ffe.m new file mode 100644 index 0000000..f9ff57f --- /dev/null +++ b/Functions/EQ_structures/ffe.m @@ -0,0 +1,163 @@ +function [ffe_results] = ffe(eq_, M, rx_signal, tx_symbols, tx_bits, options) +% FFE Processes signals through FFE equalizer +% +% 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.showAnalysis = 0; + options.eth_style_symbol_mapping = 0; + options.postFFE = []; + options.database = []; +end + +%% Process signals through equalizer +% FFE or VNLE +[eq_signal_sd, eq_noise] = eq_.process(rx_signal, tx_symbols); + +% Apply post-FFE if provided +if ~isempty(options.postFFE) + tic + [eq_signal_sd, eq_noise] = options.postFFE.process(eq_signal_sd, tx_symbols); + toc +end + +% Hard decision on FFE output +eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); + +%% 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); + +%% Calculate performance metrics +[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); +[gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); +air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M)); +[evm_total, evm_lvl] = calc_evm(eq_signal_sd, tx_symbols); +[std_total, std_lvl] = calc_std(eq_signal_sd, tx_symbols); +[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols); + +%% Display analysis if requested +if options.showAnalysis + displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, options.postFFE); +end + + +%% Prepare output structure +% Determine postFFE order +if ~isempty(options.postFFE) + npostFFE = options.postFFE.order; +else + npostFFE = 0; +end + +% Create FFE results structure +ffe_results = struct(); + +eq_.e = []; +eq_.e2 = []; +eq_.e3 = []; +ffe_results.config = Equalizerstruct(); +ffe_results.config.eq = jsonencode(eq_); +ffe_results.config.equalizer_structure = int32(equalizer_structure.ffe); +ffe_results.config.comment = 'function: ffe'; + +ffe_results.metrics = Metricstruct; +ffe_results.metrics.result_id = NaN; +ffe_results.metrics.run_id = NaN; +ffe_results.metrics.eqParam_id = NaN; +ffe_results.metrics.date_of_processing = datetime('now'); +ffe_results.metrics.BER = ber; +ffe_results.metrics.numBits = bits; +ffe_results.metrics.numBitErr = errors; +ffe_results.metrics.BER_precoded = ber_precoded; +ffe_results.metrics.numBitErr_precoded = errors_precoded; +ffe_results.metrics.SNR = snr; +ffe_results.metrics.SNR_level = snr_lvl; +ffe_results.metrics.STD = std_total; +ffe_results.metrics.STD_level = std_lvl; +ffe_results.metrics.STDrx = std_rxraw_total; +ffe_results.metrics.STDrx_level = std_rxraw_lvl; +ffe_results.metrics.GMI = gmi; +ffe_results.metrics.AIR = air; +ffe_results.metrics.EVM = evm_total; +ffe_results.metrics.EVM_level = evm_lvl; + + + +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 displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, postFFE) +% Display analysis plots and metrics +figure(336); +showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after FFE'); + +if ~isempty(postFFE) + showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338); +end + +showEQfilter(eq_.e, eq_signal_sd.fs.*2); + +figure(341); clf; +showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 341); + +warning off +figure(400); clf; +showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400); + +figure(401); clf; +showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401); +warning on +end \ No newline at end of file diff --git a/Functions/EQ_structures/vnle.m b/Functions/EQ_structures/vnle.m deleted file mode 100644 index f143452..0000000 --- a/Functions/EQ_structures/vnle.m +++ /dev/null @@ -1,192 +0,0 @@ -function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options) - -arguments - eq_ - M - rx_signal - tx_symbols - tx_bits - options.precode_mode db_mode - options.showAnalysis = 0; - options.eth_style_symbol_mapping = 0; - options.postFFE = []; - options.database = []; -end - -mudc_given = eq_.mu_dc; - -%FFE or VNLE -[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols); - -if ~isempty(options.postFFE) - tic - [eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols); - toc -end - -eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd); - -% precoding to mitigate error propagation, most prominently used in -% combination with duobinary signaling to avoid catastrophic error -% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling) - -switch options.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 = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); - - rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_precoded); - [~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle.signal,tx_bits_precoded.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1); - - %B) Just determine BER - rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); - [bits_vnle,errors_vnle,ber_vnle,error_pos_vnle] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1); - - max_burst_length = 10; - burst_count = count_error_bursts(error_pos_vnle, max_burst_length); - - 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! - 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_vnle_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_decoded); - [~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle_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 = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols); - rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); - [bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1); - - -end - -% METRICS OF VNLE SD Signal: -[snr_vnle,snr_vnle_lvl] = calc_snr(tx_symbols.signal,eq_noise.signal); -[gmi_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000); -air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M)); -[evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols); -[std_vnle_total,std_vnle_lvl] = calc_std(eq_signal_sd,tx_symbols); -[std_rxraw_total,std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols); - -eq_package.ber_vnle = ber_vnle; -eq_package.evm_vnle_total = evm_vnle_total; -eq_package.evm_vnle_lvl = evm_vnle_lvl; -eq_package.gmi = gmi_vnle; - -eq_package.eq = eq_; - -resultsVNLE = struct( ... - 'result_id', NaN, ... % - 'run_id', NaN, ... % Beispielhafte Run-ID - 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle - 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit - 'BER', ber_vnle, ... % BER = 120 / 1.000.000 - 'numBits', bits_vnle, ... % Beispiel: 1.000.000 Bits - 'numBitErr', errors_vnle, ... % Beispiel: 120 Bitfehler - 'BER_precoded', ber_vnle_diff_precoded, ... % BER = 120 / 1.000.000 - 'numBitErr_precoded', errors_vnle_diff_precoded, ... % Beispiel: 120 Bitfehler - 'SNR', snr_vnle, ... % Beispielhafte SNR - 'SNR_level', jsonencode(snr_vnle_lvl), ... % SNR-Level als JSON-codiertes Array - 'STD', std_vnle_total, ... - 'STD_level', jsonencode(std_vnle_lvl),... - 'STDrx' , std_rxraw_total, ... - 'STDrx_level', jsonencode(std_rxraw_lvl),... - 'GMI', gmi_vnle, ... % Beispielhafter GMI-Wert - 'AIR', air_vnle, ... % Beispielhafter AIR-Wert - 'EVM', evm_vnle_total, ... % Beispielhafte EVM - 'EVM_level', jsonencode(evm_vnle_lvl), ... % EVM-Level als JSON-codiertes Array - 'Alpha', [] ... % Beispielhafter Alpha-Wert - ); - -if ~isempty(options.postFFE) - npostFFE = options.postFFE.order; -else - npostFFE = 0; -end - -equalizerConfigVNLE = struct( ... - 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt - 'equalizer_structure', int32(equalizer_structure.vnle), ... % Beispiel: 1 (z.B. für vnle) - 'M', M, ... % Ordnung der PAM-Konstellation - 'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String - 'db_target', 0, ... % 0 oder 1 - 'diff_precode', int32(options.precode_mode), ... % 0 oder 1 - 'postFFE', int32(~isempty(options.postFFE)), ... % Beispielwert - 'NpostFFE', npostFFE, ... % Beispielwert - 'Ne1', eq_.order, ... % Feedforward Koeffizienten 1. Ordnung - 'K', eq_.sps, ... % Samples pro Symbol - 'DCmu', mudc_given, ... % Anpassungsrate für DC-Tap - 'training_length', eq_.len_tr, ... % Anzahl Trainingssymbole - 'training_loops', eq_.epochs_tr, ... % Anzahl Trainingsdurchläufe - 'TRmu1', eq_.mu_tr, ... % mu für DD-Modus (1. Ordnung) - 'dd_loops', eq_.epochs_dd, ... % Anzahl Durchläufe im DD-Modus - 'DDmu1', eq_.mu_dd, ... % mu für DD-Modus (1. Ordnung) - 'comment', 'function: ffe dc removal', ... % Zusätzliche Kommentare - 'ffe_buffer_len', eq_.ffe_buffer_len, ... - 'smoothing_buffer_len', eq_.smoothing_buffer_length, ... - 'smoothing_buffer_update', eq_.smoothing_buffer_update, ... - 'dc_buffer_len', eq_.dc_buffer_len, ... - 'config_hash', NaN ... - ); - -eq_package.resultsVNLE = resultsVNLE; -eq_package.equalizerConfigVNLE = equalizerConfigVNLE; - - -% eq_package.vnle_out = eq_signal_sd; -if options.showAnalysis - - - % fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl); - - % fprintf('VNLE BER: %.2e \n',ber_vnle); - % - % fprintf('MLSE BER: %.2e \n',ber_mlse); - - figure(336); - showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE'); - - % figure(337);clf; - % rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal'); - - % figure(338);clf; - % showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients','fignum',338); - - if ~isempty(options.postFFE) - showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients','fignum',338); - end - - showEQfilter(eq_.e,eq_signal_sd.fs.*2); - - % figure(340);clf; - % eq_signal_sd.eye(eq_signal_sd.fs,M,"fignum",340); - - figure(341);clf; - showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",341); - - figure(400);clf; - warning off - showLevelScatter(eq_signal_sd,tx_symbols,"fignum",400); - - showLevelScatter(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols,"fignum",400); - warning on - % autoArrangeFigures(3,3,2) - - -end - -end - diff --git a/Functions/EQ_structures/vnle_postfilter_mlse.m b/Functions/EQ_structures/vnle_postfilter_mlse.m index 85a17e1..90691d8 100644 --- a/Functions/EQ_structures/vnle_postfilter_mlse.m +++ b/Functions/EQ_structures/vnle_postfilter_mlse.m @@ -1,4 +1,19 @@ -function [eq_package] = vnle_postfilter_mlse(eq_,pf_,mlse_,M,rx_signal,tx_symbols,tx_bits,options) +function [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, rx_signal, tx_symbols, tx_bits, options) +% VNLE_POSTFILTER_MLSE Processes signals through VNLE, postfilter, and MLSE +% +% Inputs: +% eq_ - Equalizer object +% pf_ - Postfilter object +% mlse_ - MLSE 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/VNLE processing +% mlse_results - Results from MLSE processing arguments eq_ @@ -15,274 +30,202 @@ arguments options.database = []; end -%FFE or VNLE -[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols); +%% Process signals through equalizers +% FFE or VNLE +[eq_signal_sd, eq_noise] = eq_.process(rx_signal, tx_symbols); +% Apply post-FFE if provided if ~isempty(options.postFFE) tic - [eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols); + [eq_signal_sd, eq_noise] = options.postFFE.process(eq_signal_sd, tx_symbols); toc end -eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd); - -mlse_sig_sd = pf_.process(eq_signal_sd,eq_noise); +% Hard decision on VNLE output +eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); +% Process through postfilter and MLSE +mlse_sig_sd = pf_.process(eq_signal_sd, eq_noise); mlse_.DIR = pf_.coefficients; -% [mlse_sig_hd,mlse_sig_sd] = mlse_.process(mlse_sig_sd,tx_symbols); mlse_sig_sd = mlse_.process(mlse_sig_sd); +mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).quantize(mlse_sig_sd); -mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd); +%% Calculate BER based on precoding mode +[numbits, errors, bers, ~] = calculateBER(eq_signal_hd, mlse_sig_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping); -% precoding to mitigate error propagation, most prominently used in -% combination with duobinary signaling to avoid catastrophic error -% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling) +%% Calculate performance metrics +% VNLE metrics +[snr_vnle, snr_vnle_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); +[gmi_vnle] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); +air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M)); +[evm_vnle_total, evm_vnle_lvl] = calc_evm(eq_signal_sd, tx_symbols); +[std_vnle_total, std_vnle_lvl] = calc_std(eq_signal_sd, tx_symbols); +[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols); -switch options.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); - - 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(tx_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_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_precoded); - [~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded); - [~,errors_mlse_diff_precoded,ber_mlse_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_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); - [bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); - [bits_mlse,errors_mlse,ber_mlse,~] = 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! - 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_vnle_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_decoded); - [~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - 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",options.eth_style_symbol_mapping).demap(mlse_sig_hd_decoded); - [~,errors_mlse_diff_precoded,ber_mlse_diff_precoded,~] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - % B) Omit the Coding by comparing with demapped TX symbol sequence - - tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols); - rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); - [bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); - [bits_mlse,errors_mlse,ber_mlse,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - -end - -% METRICS OF VNLE SD Signal: -[snr_vnle,snr_vnle_lvl] = calc_snr(tx_symbols.signal,eq_noise.signal); -[gmi_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000); -air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M)); -[evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols); -[std_vnle_total,std_vnle_lvl] = calc_std(eq_signal_sd,tx_symbols); -[std_rxraw_total,std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols); - -% METRICS OF MLSE (HD-VITERBI) -pf_.ncoeff = 1; -pf_.process(eq_signal_sd,eq_noise); +% MLSE metrics alpha = pf_.coefficients(2); -eq_package.ber_mlse = ber_mlse; -eq_package.ber_vnle = ber_vnle; -eq_package.evm_vnle_total = evm_vnle_total; -eq_package.evm_vnle_lvl = evm_vnle_lvl; -eq_package.gmi = gmi_vnle; - -eq_package.eq = eq_; -eq_package.pf = pf_; -eq_package.mlse = mlse_; - -resultsVNLE = struct( ... - 'result_id', NaN, ... % - 'run_id', NaN, ... % Beispielhafte Run-ID - 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle - 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit - 'BER', ber_vnle, ... % BER = 120 / 1.000.000 - 'numBits', bits_vnle, ... % Beispiel: 1.000.000 Bits - 'numBitErr', errors_vnle, ... % Beispiel: 120 Bitfehler - 'BER_precoded', ber_vnle_diff_precoded, ... % BER = 120 / 1.000.000 - 'numBitErr_precoded', errors_vnle_diff_precoded, ... % Beispiel: 120 Bitfehler - 'SNR', snr_vnle, ... % Beispielhafte SNR - 'SNR_level', jsonencode(snr_vnle_lvl), ... % SNR-Level als JSON-codiertes Array - 'STD', std_vnle_total, ... - 'STD_level', jsonencode(std_vnle_lvl),... - 'STDrx' , std_rxraw_total, ... - 'STDrx_level', jsonencode(std_rxraw_lvl),... - 'GMI', gmi_vnle, ... % Beispielhafter GMI-Wert - 'AIR', air_vnle, ... % Beispielhafter AIR-Wert - 'EVM', evm_vnle_total, ... % Beispielhafte EVM - 'EVM_level', jsonencode(evm_vnle_lvl), ... % EVM-Level als JSON-codiertes Array - 'Alpha', [] ... % Beispielhafter Alpha-Wert - ); - +%% Prepare output structures +% Determine postFFE order if ~isempty(options.postFFE) npostFFE = options.postFFE.order; else npostFFE = 0; end -equalizerConfigVNLE = struct( ... - 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt - 'equalizer_structure', int32(equalizer_structure.vnle), ... % Beispiel: 1 (z.B. für vnle) - 'M', M, ... % Ordnung der PAM-Konstellation - 'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String - 'db_target', 0, ... % 0 oder 1 - 'diff_precode', int32(options.precode_mode), ... % 0 oder 1 - 'postFFE', ~isempty(options.postFFE), ... % Beispielwert - 'NpostFFE', npostFFE, ... % Beispielwert - 'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung - 'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung - 'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung - 'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung - 'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung - 'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung - 'K', eq_.K, ... % Samples pro Symbol - 'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap - 'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1) - 'training_length', eq_.training_length, ... % Anzahl Trainingssymbole - 'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe - 'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung) - 'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung) - 'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung) - 'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD - 'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus - 'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung) - 'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung) - 'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung) - 'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD - 'comment', 'function: vnle_postfilter_mlse', ... % Zusätzliche Kommentare - 'config_hash', NaN ... - ); +ffe_results = struct(); +ffe_results.metrics = Metricstruct; +ffe_results.metrics.result_id = NaN; +ffe_results.metrics.run_id = NaN; +ffe_results.metrics.eqParam_id = NaN; +ffe_results.metrics.date_of_processing = datetime('now'); +ffe_results.metrics.BER = bers.vnle; +ffe_results.metrics.numBits = numbits.vnle; +ffe_results.metrics.numBitErr = errors.vnle; +ffe_results.metrics.BER_precoded = bers.vnle_precoded; +ffe_results.metrics.numBitErr_precoded = errors.vnle_precoded; +ffe_results.metrics.SNR = snr_vnle; +ffe_results.metrics.SNR_level = snr_vnle_lvl; +ffe_results.metrics.STD = std_vnle_total; +ffe_results.metrics.STD_level = std_vnle_lvl; +ffe_results.metrics.STDrx = std_rxraw_total; +ffe_results.metrics.STDrx_level = std_rxraw_lvl; +ffe_results.metrics.GMI = gmi_vnle; +ffe_results.metrics.AIR = air_vnle; +ffe_results.metrics.EVM = evm_vnle_total; +ffe_results.metrics.EVM_level = evm_vnle_lvl; +ffe_results.metrics.Alpha = []; -resultsMLSE = struct( ... - 'result_id', NaN, ... % - 'run_id', NaN, ... % Beispielhafte Run-ID - 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle - 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit - 'BER', ber_mlse, ... % BER = 120 / 1.000.000 - 'numBits', bits_mlse, ... % Beispiel: 1.000.000 Bits - 'numBitErr', errors_mlse, ... % Beispiel: 120 Bitfehler - 'BER_precoded', ber_mlse_diff_precoded, ... % BER = 120 / 1.000.000 - 'numBitErr_precoded', errors_mlse_diff_precoded, ... % Beispiel: 120 Bitfehler - 'SNR', [], ... % Beispielhafte SNR - 'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array - 'GMI', [], ... % Beispielhafter GMI-Wert - 'AIR', [], ... % Beispielhafter AIR-Wert - 'EVM', [], ... % Beispielhafte EVM - 'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array - 'Alpha', alpha, ... % Beispielhafter Alpha-Wert - 'MLSE_dir', jsonencode([mlse_.DIR])... - ); +% Create FFE results structure +eq_.e = []; +eq_.e2 = []; +eq_.e3 = []; +ffe_results.config = Equalizerstruct(); +ffe_results.config.eq = jsonencode(eq_); +ffe_results.config.equalizer_structure = int32(equalizer_structure.vnle); +ffe_results.config.comment = 'function: vnle_postfilter_mlse - FFE part'; -equalizerConfigMLSE = struct( ... - 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt - 'equalizer_structure', int32(equalizer_structure.vnle_pf_mlse), ... % Beispiel: 1 (z.B. für vnle) - 'M', M, ... % Ordnung der PAM-Konstellation - 'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String - 'db_target', 0, ... % 0 oder 1 - 'diff_precode', int32(options.precode_mode), ... % 0 oder 1 - 'postFFE', ~isempty(options.postFFE), ... % Beispielwert - 'NpostFFE', npostFFE, ... % Beispielwert - 'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung - 'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung - 'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung - 'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung - 'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung - 'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung - 'K', eq_.K, ... % Samples pro Symbol - 'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap - 'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1) - 'training_length', eq_.training_length, ... % Anzahl Trainingssymbole - 'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe - 'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung) - 'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung) - 'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung) - 'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD - 'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus - 'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung) - 'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung) - 'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung) - 'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD - 'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String - 'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert - 'comment', 'function: vnle_postfilter_mlse', ... % Zusätzliche Kommentare - 'config_hash', NaN ... - ); -eq_package.resultsVNLE = resultsVNLE; -eq_package.resultsMLSE = resultsMLSE; -eq_package.equalizerConfigVNLE = equalizerConfigVNLE; -eq_package.equalizerConfigMLSE = equalizerConfigMLSE; +mlse_results = struct(); +mlse_results.metrics = Metricstruct; +mlse_results.metrics.result_id = NaN; +mlse_results.metrics.run_id = NaN; +mlse_results.metrics.eqParam_id = NaN; +mlse_results.metrics.date_of_processing = datetime('now'); +mlse_results.metrics.BER = bers.mlse; +mlse_results.metrics.numBits = numbits.mlse; +mlse_results.metrics.numBitErr = errors.mlse; +mlse_results.metrics.BER_precoded = bers.mlse_precoded; +mlse_results.metrics.numBitErr_precoded = errors.mlse_precoded; +mlse_results.metrics.SNR = NaN; +mlse_results.metrics.SNR_level = NaN; +mlse_results.metrics.GMI = NaN; +mlse_results.metrics.AIR = NaN; +mlse_results.metrics.EVM = NaN; +mlse_results.metrics.EVM_level = NaN; +mlse_results.metrics.Alpha = alpha; +mlse_results.metrics.MLSE_dir = mlse_.DIR; +% Create MLSE results structure +mlse_results.config = Equalizerstruct(); +mlse_results.config.eq = jsonencode(eq_); +mlse_.DIR = []; +mlse_results.config.mlse = jsonencode(mlse_); +mlse_results.config.equalizer_structure = int32(equalizer_structure.vnle_pf_mlse); +mlse_results.config.comment = 'function: vnle_postfilter_mlse - MLSE part'; -% eq_package.vnle_out = eq_signal_sd; +%% Display analysis if requested if options.showAnalysis - - - % fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl); - - % fprintf('VNLE BER: %.2e \n',ber_vnle); - % - % fprintf('MLSE BER: %.2e \n',ber_mlse); - - figure(336); - showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE','postfilter_taps',pf_.coefficients); - - % figure(337);clf; - % rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal'); - - % figure(338);clf; - % showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients','fignum',338); - - if ~isempty(options.postFFE) - showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients','fignum',338); - end - - showEQfilter(eq_.e,eq_signal_sd.fs.*2); - - figure(340);clf; - eq_signal_sd.eye(eq_signal_sd.fs,M,"fignum",340); - - figure(341);clf; - showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",341); - - warning off - showLevelScatter(eq_signal_sd,tx_symbols,"fignum",400); - - showLevelScatter(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols,"fignum",401); - warning on - drawnow; - - % autoArrangeFigures(3,3,2) - + displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, options.postFFE); +end end +%% Helper Functions +function [numbits, errors, ber, error_locations] = calculateBER(eq_signal_hd, mlse_sig_hd, tx_symbols, tx_bits, precode_mode, M, eth_style) +% Initialize output structure +numbits = struct('vnle', 0, 'mlse', 0); +errors = struct('vnle', 0, 'mlse', 0, 'vnle_precoded', 0, 'mlse_precoded', 0); +ber = struct('vnle', 0, 'mlse', 0, 'vnle_precoded', 0, 'mlse_precoded', 0); +error_locations = struct('vnle', [], 'mlse', []); + +% PAM mapper for demapping +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); + + 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(tx_symbols); + tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); + + tx_bits_precoded = mapper.demap(tx_symbols_precoded); + + rx_bits_vnle = mapper.demap(eq_signal_hd_precoded); + [~, errors.vnle_precoded, ber.vnle_precoded, ~] = calc_ber(rx_bits_vnle.signal, tx_bits_precoded.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); + + rx_bits_mlse = mapper.demap(mlse_sig_hd_precoded); + [~, errors.mlse_precoded, ber.mlse_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_vnle = mapper.demap(eq_signal_hd); + [numbits.vnle, errors.vnle, ber.vnle, error_locations.vnle] = calc_ber(rx_bits_vnle.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); + + rx_bits_mlse = mapper.demap(mlse_sig_hd); + [numbits.mlse, errors.mlse, ber.mlse, error_locations.mlse] = calc_ber(rx_bits_mlse.signal, tx_bits.signal, "skip_front", 100, "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_vnle_decoded = mapper.demap(eq_signal_hd_decoded); + [~, errors.vnle_precoded, ber.vnle_precoded, ~] = calc_ber(rx_bits_vnle_decoded.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); + + 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 = mapper.demap(mlse_sig_hd_decoded); + [~, errors.mlse_precoded, ber.mlse_precoded, ~] = calc_ber(rx_bits_mlse_decoded.signal, tx_bits.signal, "skip_front", 100, "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_vnle = mapper.demap(eq_signal_hd); + [numbits.vnle, errors.vnle, ber.vnle, error_locations.vnle] = calc_ber(rx_bits_vnle.signal, tx_bits_demapped.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); + + rx_bits_mlse = mapper.demap(mlse_sig_hd); + [numbits.mlse, errors.mlse, ber.mlse, error_locations.mlse] = calc_ber(rx_bits_mlse.signal, tx_bits_demapped.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); +end +end + + +function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, postFFE) +% Display analysis plots and metrics +figure(336); +showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients); + +if ~isempty(postFFE) + showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338); +end + +showEQfilter(eq_.e, eq_signal_sd.fs.*2); + +figure(340); clf; +eq_signal_sd.eye(eq_signal_sd.fs, M, "fignum", 340); + +figure(341); clf; +showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 341); + +warning off +showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400); +showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401); +drawnow; +warning on end \ No newline at end of file diff --git a/Functions/EQ_visuals/showLevelHistogram.m b/Functions/EQ_visuals/showLevelHistogram.m index 66e5d08..437b65c 100644 --- a/Functions/EQ_visuals/showLevelHistogram.m +++ b/Functions/EQ_visuals/showLevelHistogram.m @@ -40,7 +40,7 @@ end cnt(lvl) = round(numel(intermediate(~isnan(intermediate)))./length(eq_signal),3).*100; hold on warning off - histogram(received_sd(lvl,:),1000,"EdgeAlpha",0,'DisplayName',['Lvl ',num2str(lvl),' | ',num2str(cnt(lvl)),' %'],'FaceColor',lvlcol(lvl,:),'Normalization','pdf'); + histogram(received_sd(lvl,:),1000,"EdgeAlpha",0,'DisplayName',['Lvl ',num2str(lvl),' ; ',num2str(cnt(lvl)),' '],'FaceColor',lvlcol(lvl,:),'Normalization','pdf'); warning on end legend diff --git a/Functions/EQ_visuals/showLevelScatter.m b/Functions/EQ_visuals/showLevelScatter.m index 1bc7e33..a5d34ef 100644 --- a/Functions/EQ_visuals/showLevelScatter.m +++ b/Functions/EQ_visuals/showLevelScatter.m @@ -1,4 +1,4 @@ -function showLevelScatter(eq_signal,ref_symbols,options) +function [symbols_for_lvl,avg_for_lvl] = showLevelScatter(eq_signal,ref_symbols,options) arguments eq_signal ref_symbols @@ -7,22 +7,27 @@ arguments options.f_sym = []; end +plot_shit = 0; + if isa(eq_signal,'Signal') options.f_sym = eq_signal.fs; eq_signal = eq_signal.signal; + assert(~isempty(options.f_sym),'No fsym given'); end if isa(ref_symbols,'Signal') ref_symbols = ref_symbols.signal; end -% Determine the figure number to use or create a new figure -if isnan(options.fignum) - fig = figure; % Create a new figure and get its handle -else - fig = figure(options.fignum); % Use the specified figure number + +if plot_shit + % Determine the figure number to use or create a new figure + if isnan(options.fignum) + fig = figure; % Create a new figure and get its handle + else + fig = figure(options.fignum); % Use the specified figure number + end end -assert(~isempty(options.f_sym),'No fsym given'); rx_symbols = eq_signal ./ rms(eq_signal); correct_symbols = ref_symbols; @@ -32,57 +37,54 @@ col = cbrewer2('Paired',numel(unique(correct_symbols))*2); ccnt = -1; levels = unique(correct_symbols); - +symbols_for_lvl = NaN(numel(levels),length(correct_symbols)); start = 1; ende = length(correct_symbols); -% start = 30000; -% ende = 40000; - for l = 1:numel(levels) ccnt = ccnt+2; level_amplitude = levels(l); - symbols_for_lvl = NaN(1,length(correct_symbols)); - symbols_for_lvl(correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude); - std_lvl(l) = std(symbols_for_lvl,'omitnan'); + + symbols_for_lvl(l,correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude); + std_lvl(l) = std(symbols_for_lvl(l,:),'omitnan'); xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6; - % xax_in_sec = 1:length(correct_symbols); -if 0 - scatter(xax_in_sec(start:ende),symbols_for_lvl(start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:)); - hold on; -end + + if plot_shit + scatter(xax_in_sec(start:ende),symbols_for_lvl(l,start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:)); + hold on; + end + 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); - symbols_for_lvl = NaN(1,length(correct_symbols)); - movmean = 1/250 .* movsum(rx_symbols(correct_symbols==level_amplitude),[250/2,250/2], 'Endpoints', 'fill'); - symbols_for_lvl(correct_symbols==level_amplitude) = movmean; + avg_for_lvl(l,correct_symbols==level_amplitude) = movmean; - nanx = isnan(symbols_for_lvl); - t = 1:numel(symbols_for_lvl); - symbols_for_lvl(nanx) = interp1(t(~nanx), symbols_for_lvl(~nanx), t(nanx)); + 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)); xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6; % xax_in_sec = 1:length(correct_symbols); - plot(xax_in_sec(start:ende),symbols_for_lvl(start:ende),'Color',col(ccnt,:)); + if plot_shit + plot(xax_in_sec(start:ende),avg_for_lvl(l,start:ende),'Color',col(ccnt,:)); + end hold on end -%yline(max(rx_symbols(correct_symbols==levels(2)))) -yline(levels); + if 0 annotation(fig,'textbox',... @@ -121,9 +123,9 @@ if 0 'FitBoxToText','off'); end -% xlim([0, 2.6]) -% ylim([-2 2]) +if plot_shit +yline(levels); xlabel('Time in $\mu$s'); ylabel('Normalized Amplitude'); - +end end diff --git a/Functions/Job_Processing/configureEqualizers_remove.m b/Functions/Job_Processing/configureEqualizers_remove.m new file mode 100644 index 0000000..f207088 --- /dev/null +++ b/Functions/Job_Processing/configureEqualizers_remove.m @@ -0,0 +1,40 @@ +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/loadSignalData.m b/Functions/Job_Processing/loadSignalData.m new file mode 100644 index 0000000..cff4ff8 --- /dev/null +++ b/Functions/Job_Processing/loadSignalData.m @@ -0,0 +1,106 @@ +function [Bits, Symbols, Scpe_cell, found_sync] = loadSignalData(dataTable, options) +% LOADSIGNALDATA Loads and synchronizes signal data from storage +% +% Inputs: +% dataTable - Table with file paths and configuration +% options - Struct with storage_path and max_occurences +% +% Outputs: +% Symbols_mapped - Mapped symbols from bits +% Symbols - Original symbols +% Scpe_cell - Cell array of synchronized signals +% found_sync - Boolean indicating if synchronization was successful + +found_sync = 0; +tempLocalStorage = 1; + +% Part A: Check and load from local storage if available +if tempLocalStorage == 1 + local_filename = sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id)); + if exist(local_filename, 'file') + % Load from local storage and return + load(local_filename, 'Bits', 'Symbols', 'Scpe_cell'); + found_sync = 1; + end +end + +% If not locally saved, load from storage +if ~found_sync + % Load transmitted bits + Bits = load([options.storage_path, char(dataTable.tx_bits_path)]); + Bits = Bits.Bits; + + % Map bits to symbols + M = double(dataTable.pam_level); + fsym = dataTable.symbolrate; + Symbols_mapped = PAMmapper(M,0).map(Bits); + Symbols_mapped.fs = fsym; + + % Load original symbols + Symbols = load([options.storage_path, char(dataTable.tx_symbols_path)]); + Symbols = Symbols.Symbols; + + found_sync = 0; + Scpe_cell = {}; + + % Try to load pre-synchronized data + try + Scpe_load = load([options.storage_path, char(dataTable.rx_sync_path)]); + Scpe_cell = Scpe_load.S; + [~,~,~,found_sync] = Scpe_cell{2}.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + catch + % Continue to next method if this fails + end +end + +% If not found, try with raw data +if ~found_sync + try + Scpe_sig_raw = load([options.storage_path, char(dataTable.rx_raw_path(1))]); + Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; + Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in", Scpe_sig_raw.fs, "fs_out", 2*fsym); + [~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + catch + % Continue to next method if this fails + end +end + +% Last attempt with mapped symbols +if ~found_sync && exist('Scpe_sig_raw', 'var') + if length(Symbols_mapped.signal) ~= sum(Symbols_mapped.signal == Symbols.signal) + [~, Scpe_cell, ~, found_sync] = Scpe_sig_raw.tsynch("reference", Symbols_mapped, "fs_ref", fsym, "debug_plots", 0); + end +end + +% Part B: Save to local storage if data was loaded and synced +if tempLocalStorage == 1 && found_sync + local_filename = sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id)); + + % Create directory if it doesn't exist + if ~exist('temp_sync_data', 'dir') + mkdir('temp_sync_data'); + end + + % List existing files and remove oldest if more than N + max_local_files = 10; % Store up to N files + files = dir('sync_data_run_*.mat'); + if length(files) >= max_local_files + % Sort by date + [~, idx] = sort([files.datenum]); + % Delete oldest file + delete(files(idx(1)).name); + end + + % Save current data + save(local_filename, 'Bits', 'Symbols', 'Scpe_cell'); +end + +% Limit number of occurrences +if found_sync + record_realizations = min(options.max_occurences, length(Scpe_cell)); + Scpe_cell = Scpe_cell(1:record_realizations); +else + warning('Could not synchronize the received signal with the stored symbols!'); +end + +end \ No newline at end of file diff --git a/Functions/Job_Processing/preprocessSignal.m b/Functions/Job_Processing/preprocessSignal.m new file mode 100644 index 0000000..4828bba --- /dev/null +++ b/Functions/Job_Processing/preprocessSignal.m @@ -0,0 +1,26 @@ +function Scpe_sig = preprocessSignal(Scpe_sig, Symbols, fsym) +% PREPROCESSSIGNAL Performs standard preprocessing on a signal +% +% Inputs: +% Scpe_sig - Input signal +% Symbols - Reference symbols for synchronization +% fsym - Symbol frequency +% +% Outputs: +% Scpe_sig - Preprocessed signal + +% Resample to 2x symbol rate +Scpe_sig = Scpe_sig.resample("fs_out", 2*fsym); + +% Synchronize with reference +[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); + +% Remove DC offset +Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); + +end \ No newline at end of file diff --git a/Functions/Job_Processing/printResults.m b/Functions/Job_Processing/printResults.m new file mode 100644 index 0000000..4cd1ddc --- /dev/null +++ b/Functions/Job_Processing/printResults.m @@ -0,0 +1,47 @@ +function printResults(run_id, M, Symbols, vnle_pf_package, dbtgt_package, occ) + % PRINTRESULTS Prints formatted results from equalization + % + % Inputs: + % run_id - Run identifier + % M - PAM level + % Symbols - Symbol data with fs property + % vnle_pf_package - VNLE+PF results package + % dbtgt_package - DB target results package (optional) + % occ - Occurrence index + + % Print header + fprintf("==== EQUALIZATION RUN-ID %d | PAM-%d | %.2f GBd ====\n\n", run_id, M, Symbols.fs.*1e-9); + + % VNLE Results + if ~isempty(vnle_pf_package) + vnle_result = vnle_pf_package{occ}.resultsVNLE; + mlse_result = vnle_pf_package{occ}.resultsMLSE; + + fprintf(">> VNLE Results:\n"); + fprintf(" BER: %.2e\n", vnle_result.BER); + fprintf(" BER (pre-code): %.2e\n", vnle_result.BER_precoded); + fprintf(" SNR: %.2f dB\n", vnle_result.SNR); + fprintf(" GMI: %.4f\n", vnle_result.GMI); + fprintf(" Linerate: %.2f Gbps\n", Symbols.fs .* floor(log2(M)*10)/10 .*1e-9); + fprintf(" AIR: %.2f Gbps\n", vnle_result.AIR.*1e-9); + fprintf("\n"); + + % MLSE Results + fprintf(">> MLSE Results:\n"); + fprintf(" BER: %.2e\n", mlse_result.BER); + fprintf(" BER (pre-code): %.2e\n", mlse_result.BER_precoded); + fprintf(" Channel Alpha: %.2f\n", mlse_result.Alpha); + fprintf("\n"); + end + + % DB Target Results + if ~isempty(dbtgt_package) + dbtgt = dbtgt_package{occ}.resultsDBtgt; + fprintf(">> DB Target Results:\n"); + fprintf(" BER: %.2e\n", dbtgt.BER); + fprintf(" BER (pre-code): %.2e\n", dbtgt.BER_precoded); + fprintf("\n"); + end + + fprintf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n\n"); + end \ No newline at end of file diff --git a/Functions/Job_Processing/queryRunid.m b/Functions/Job_Processing/queryRunid.m new file mode 100644 index 0000000..7a0e048 --- /dev/null +++ b/Functions/Job_Processing/queryRunid.m @@ -0,0 +1,11 @@ +function dataTable = queryRunid(run_id, database) + + % Query database for run configuration + filterParams = database.tables; + filterParams.Runs = struct('run_id', run_id); + + [dataTable, ~] = database.queryDB(filterParams, database.getTableFieldNames('Runs')); + [~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices + dataTable = dataTable(uniqueIdx, :); % Extract unique configurations for each run_id + +end \ No newline at end of file diff --git a/Functions/Job_Processing/submitJobs.m b/Functions/Job_Processing/submitJobs.m new file mode 100644 index 0000000..a5a45a4 --- /dev/null +++ b/Functions/Job_Processing/submitJobs.m @@ -0,0 +1,213 @@ +function [results,wh] = submitJobs(run_ids, dsp_options, submit_mode, submit_options) +% SUBMITJOBS Submits dsp_runid jobs for processing +% +% Inputs: +% run_ids - Single run ID or array of Run IDs for processing +% dsp_options - Options for dsp_runid function +% submit_mode - Execution mode: 'parallel' or 'linear' +% options - Optional parameters +% +% Outputs: +% results - Cell array of results from each job +% wh - Updated DataStorage object + +% USAGE: +% % === SETTINGS === +% +% dsp_options.append_to_db = 1; +% dsp_options.max_occurences = 15; +% dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +% dsp_options.database_name = 'silas_labor_newdsp_newstructure.db'; +% dsp_options.storage_path = 'Z:\2024\sioe_labor\'; +% dsp_options.parameters = struct(); +% dsp_options.parameters.mu_dc = [0.005]; +% +% % === Get Run ID's === +% db = DBHandler("pathToDB", [dsp_options.database_path, dsp_options.database_name], "type", "sqlite"); +% fp = QueryFilter(); +% % fp.where('Runs', 'run_id','EQUALS', 5108); +% fp.where('Runs', 'is_mpi','EQUALS', 0); +% fp.where('Runs', 'fiber_length','EQUALS', 1); +% fp.where('Runs', 'wavelength','EQUALS', 1310); +% fp.where('Runs', 'db_mode','EQUALS', 0); +% fp.where('Runs', 'rop_attenuation','EQUALS', 0); +% fp.where('Runs', 'pam_level','EQUALS', 4); +% fp.where('Runs', 'bitrate','EQUALS', 360e9); +% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); +% [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); + +% % === Initialize DataStorage === +% wh = DataStorage(dsp_options.parameters); +% wh.addStorage("ffe_package"); +% wh.addStorage("mlse_package"); +% wh.addStorage("vnle_package"); +% wh.addStorage("dbtgt_package"); +% wh.addStorage("dbenc_package"); +% +% % === RUN IT === +% [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); +% wh.getStoValue('ffe_package',0.005); +% wh.getStoValue('mlse_package',0.005); + + +arguments + run_ids int32 = 0 + dsp_options struct = struct(); + submit_mode processingMode = processingMode.serial + submit_options.waitbar (1,1) logical = true + submit_options.wh = DataStorage(struct()); % Optional DataStorage object +end + +% Ensure run_ids is a row vector +run_ids = run_ids(:)'; + +% Get number of jobs per run_id +nJobsPerRunId = submit_options.wh.getLastLinIndice; +nRunIds = length(run_ids); +totalJobs = nJobsPerRunId * nRunIds; + +% Initialize results array for all jobs +results = cell(nJobsPerRunId, nRunIds); +futures = cell(totalJobs, 1); + +% Initialize waitbar if requested +if submit_options.waitbar + h = waitbar(0, 'Processing Jobs...'); + cleanupObj = onCleanup(@() delete(h)); +end + +% Process jobs based on mode +if submit_mode == processingMode.parallel + setupParallelPool(); + + % Submit jobs to parallel pool + jobCounter = 0; + for r = 1:nRunIds + for k = 1:nJobsPerRunId + jobCounter = jobCounter + 1; + optionalVars = buildOptionalVars(k, submit_options.wh); + + % Submit job with proper arguments + futures{jobCounter} = parfeval(@dsp_runid, 1, run_ids(r), ... + "database_name", dsp_options.database_name, ... + "append_to_db", dsp_options.append_to_db, ... + "database_path", dsp_options.database_path, ... + "max_occurences", dsp_options.max_occurences, ... + "storage_path", dsp_options.storage_path, ... + "parameters", optionalVars); + + fprintf('[RunID %d, Job %d] Submitted to pool.\n', run_ids(r), k); + end + end + + if submit_options.waitbar + setupParallelWaitbar(futures, totalJobs, h); + end + + % Fetch results + jobCounter = 0; + for r = 1:nRunIds + for k = 1:nJobsPerRunId + jobCounter = jobCounter + 1; + try + results{k,r} = fetchOutputs(futures{jobCounter}); + fprintf('[RunID %d, Job %d] Completed successfully.\n', run_ids(r), k); + storeResult(results{k,r}, k, submit_options.wh); + catch ME + handleError(ME, k, run_ids(r)); + results{k,r} = ME; + end + end + end + +elseif submit_mode == processingMode.serial + % Process jobs sequentially + jobCounter = 0; + for r = 1:nRunIds + for k = 1:nJobsPerRunId + jobCounter = jobCounter + 1; + optionalVars = buildOptionalVars(k, submit_options.wh); + + try + fprintf('[RunID %d, Job %d] Running in linear mode...\n', run_ids(r), k); + + results{k,r} = dsp_runid(run_ids(r),... + "database_name", dsp_options.database_name,... + "append_to_db", dsp_options.append_to_db,... + "database_path", dsp_options.database_path,... + "max_occurences", dsp_options.max_occurences,... + "storage_path", dsp_options.storage_path,... + "parameters", optionalVars); + + fprintf('[RunID %d, Job %d] Completed successfully.\n', run_ids(r), k); + storeResult(results{k,r}, k, submit_options.wh); + catch ME + handleError(ME, k, run_ids(r)); + results{k,r} = ME; + end + + % Update waitbar if requested + if submit_options.waitbar + waitbar(jobCounter/totalJobs, h, sprintf('Completed %d/%d jobs', jobCounter, totalJobs)); + end + end + end +else + error('') +end + +wh = submit_options.wh; + +% Helper functions remain the same except for handleError +% Modified handleError function to include run_id + function handleError(ME, jobIndex, run_id) + fprintf('[RunID %d, Job %d] ERROR [%s]: %s\n', run_id, jobIndex, ME.identifier, ME.message); + for st = ME.stack' + fprintf(' %s:%d (%s)\n', st.file, st.line, st.name); + end + fprintf('Full report:\n%s\n', getReport(ME,'extended')); + end + +% Other helper functions remain unchanged + function setupParallelPool() + curpool = gcp('nocreate'); + if isempty(curpool) + parpool; + else + if ~isempty(curpool.FevalQueue.QueuedFutures) || ~isempty(curpool.FevalQueue.RunningFutures) + oldq = length(curpool.FevalQueue.QueuedFutures) + length(curpool.FevalQueue.RunningFutures); + curpool.FevalQueue.cancelAll; + fprintf('Canceled %d unfetched jobs from old queue.\n', oldq); + end + end + end + + function optionalVars = buildOptionalVars(jobIndex, wh) + optionalVars = struct(); + if ~isempty(wh.getDimension) + [parametervalues, parameternames] = wh.getPhysIndicesByLinIndex(jobIndex); + for pidx = 1:numel(parameternames) + optionalVars.(parameternames{pidx}) = parametervalues{pidx}; + end + end + end + + function setupParallelWaitbar(futures, totalJobs, h) + updateWaitbar = @(~) waitbar(sum(cellfun(@(f) strcmp(f.State, 'finished'), futures))/totalJobs, h, ... + sprintf('Completed %d/%d jobs', sum(cellfun(@(f) strcmp(f.State, 'finished'), futures)), totalJobs)); + + futureArray = [futures{:}]; + afterEach(futureArray, updateWaitbar, 0); + end + + function storeResult(result, jobIndex, wh) + if ~isempty(wh) + wh.addValueToStorageByLinIdx(result.ffe_package, 'ffe_package', jobIndex); + wh.addValueToStorageByLinIdx(result.mlse_package, 'mlse_package', jobIndex); + wh.addValueToStorageByLinIdx(result.vnle_package, 'vnle_package', jobIndex); + wh.addValueToStorageByLinIdx(result.dbtgt_package, 'dbtgt_package', jobIndex); + wh.addValueToStorageByLinIdx(result.dbenc_package, 'dbenc_package', jobIndex); + end + end + +end \ No newline at end of file diff --git a/Functions/Metrics/calc_evm.m b/Functions/Metrics/calc_evm.m index c597732..a8e4b7e 100644 --- a/Functions/Metrics/calc_evm.m +++ b/Functions/Metrics/calc_evm.m @@ -28,39 +28,39 @@ end [evm_total,evm_lvl] = calc_evm_(test_signal,reference_signal); -function [evm_total,evm_lvl] = calc_evm_(test_signal,reference_signal) + function [evm_total, evm_lvl] = calc_evm_(test_signal, reference_signal) + % Validate input + assert(length(test_signal) == length(reference_signal), "Sequence length does not match"); - assert(length(test_signal) == length(reference_signal),"Sequence length does not match"); + % Calculate error vector + error_vector = test_signal - reference_signal; - error_vector = (test_signal-reference_signal); + % EVM (RMS) as percentage, per MathWorks definition + evm_total = sqrt(sum(abs(error_vector).^2) / sum(abs(reference_signal).^2)) * 100; - %%% Overall EVM - evm_total = rms(error_vector); - - try - %%% Per Level EVM + % Per-level EVM k = unique(reference_signal); + evm_lvl = NaN(1, length(k)); for lvl = 1:length(k) - lvl_errors = error_vector(reference_signal==k(lvl)); - evm_lvl(lvl) = rms(lvl_errors); + idx = reference_signal == k(lvl); + if any(idx) + lvl_errors = error_vector(idx); + lvl_refs = reference_signal(idx); + evm_lvl(lvl) = sqrt(sum(abs(lvl_errors).^2) / sum(abs(lvl_refs).^2)) * 100; + end end - catch - evm_lvl = NaN; - warning('No EVM per level calculated') end -end + function [data_,reference_]=trimseq(data,reference,skipstart,skip_end) -function [data_,reference_]=trimseq(data,reference,skipstart,skip_end) + data_ = data(skipstart+1:end-skip_end,:); - data_ = data(skipstart+1:end-skip_end,:); + delta_bits = length(reference) - length(data); - delta_bits = length(reference) - length(data); + skip_end = delta_bits + skip_end; - skip_end = delta_bits + skip_end; + reference_ = reference(skipstart+1:end-skip_end,:); - reference_ = reference(skipstart+1:end-skip_end,:); - -end + end end \ No newline at end of file diff --git a/Functions/Metrics/calc_std.m b/Functions/Metrics/calc_std.m index 5f3a2e5..927b18d 100644 --- a/Functions/Metrics/calc_std.m +++ b/Functions/Metrics/calc_std.m @@ -25,34 +25,46 @@ end [test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end); % CALC EVM -[std_total,std_lvl] = calc_std_(test_signal,reference_signal); +[std_total, std_lvl, nsd_lvl, d_min]= calc_std_(test_signal,reference_signal); +std_lvl = nsd_lvl; + function [std_total, std_lvl, nsd_lvl, d_min] = calc_std_(test_signal, reference_signal) + + % NSD (Normalized Standard Deviation) expresses the noise spread at each symbol level + % relative to the minimum distance between levels. A low NSD means low error risk, + % while NSD approaching 0.5 indicates a high chance of symbol errors due to noise. -function [std_total,std_lvl] = calc_std_(test_signal,reference_signal) - - assert(length(test_signal) == length(reference_signal),"Sequence length does not match"); - - error_vector = (test_signal-reference_signal); - - %%% Overall EVM - std_total = std(test_signal); - - test_signal = test_signal ./ rms(test_signal); - - try - %%% Per Level EVM + % Ensure input is column vector + test_signal = test_signal(:); + reference_signal = reference_signal(:); + + assert(length(test_signal) == length(reference_signal), "Sequence length does not match"); + + % Find unique levels and their minimum spacing k = unique(reference_signal); + d_min = min(diff(k)); % Minimum distance between adjacent levels + + % Normalize test signal to RMS=1 (if not already) + test_signal = test_signal / rms(test_signal); + + % Overall standard deviation (not normalized) + std_total = std(test_signal); + + % Per-level standard deviation and normalized std + std_lvl = zeros(1, length(k)); + nsd_lvl = zeros(1, length(k)); for lvl = 1:length(k) - % lvl_errors = error_vector(reference_signal==k(lvl)); - std_lvl(lvl) = std(test_signal(reference_signal==k(lvl))); + idx = reference_signal == k(lvl); + if any(idx) + std_lvl(lvl) = std(test_signal(idx)); + nsd_lvl(lvl) = std_lvl(lvl) / d_min; + else + std_lvl(lvl) = NaN; + nsd_lvl(lvl) = NaN; + end end - catch - std_lvl = NaN; - warning('No EVM per level calculated') end -end - function [data_,reference_] = trimseq(data,reference,skipstart,skip_end) data_ = data(skipstart+1:end-skip_end,:); diff --git a/Functions/Theory/propagation_time.m b/Functions/Theory/propagation_time.m new file mode 100644 index 0000000..9c64897 --- /dev/null +++ b/Functions/Theory/propagation_time.m @@ -0,0 +1,11 @@ +function prop_time = propagation_time(fiber_len_m) + + c = physconst('lightspeed'); % Speed of light in vacuum (m/s) + n = 1.46; % Refractive index of the fiber + + % Calculate the propagation speed in the fiber + propagation_speed = c / n; + + % Calculate the propagation time + prop_time = fiber_len_m ./ propagation_speed; +end \ No newline at end of file diff --git a/Functions/beautifyBERplot.m b/Functions/beautifyBERplot.m index e7a02ef..0d0a665 100644 --- a/Functions/beautifyBERplot.m +++ b/Functions/beautifyBERplot.m @@ -9,9 +9,9 @@ function beautifyBERplot() 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 + % 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 diff --git a/projects/ECOC_2025/dsp_run_id.m b/projects/ECOC_2025/dsp_run_id.m index 5ef32d3..d1941a9 100644 --- a/projects/ECOC_2025/dsp_run_id.m +++ b/projects/ECOC_2025/dsp_run_id.m @@ -12,13 +12,13 @@ arguments options.storage_path end -% database = DBHandler("pathToDB",[options.database_path,options.database_name],"type","sqlite"); -database = DBHandler("type","mysql"); +database = DBHandler("pathToDB",[options.database_path,options.database_name],"type","sqlite"); +% database = DBHandler("type","mysql"); filterParams = database.tables; filterParams.Configurations = struct('run_id', run_id); -selectedFields = {'Runs.run_id','Runs.tx_bits_path','Runs.tx_signal_path','Runs.tx_symbols_path','Runs.rx_sync_path','Runs.rx_raw_path',... +selectedFields = {'Runs.run_id','Runs.tx_bits_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'}; @@ -28,8 +28,11 @@ dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each ru fsym = dataTable.symbolrate; M = double(dataTable.pam_level); +try duob_mode = db_mode.(strrep(char(dataTable.db_mode),'"','')); - +catch +duob_mode = db_mode(dataTable.db_mode); +end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% len_tr = 4096*2; @@ -82,8 +85,6 @@ dbtgt_package = {}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -Tx_signal = load([options.storage_path, char(dataTable.tx_signal_path)]); -Tx_signal = Tx_signal.Digi_sig; Tx_bits = load([options.storage_path, char(dataTable.tx_bits_path)]); Tx_bits = Tx_bits.Bits; @@ -98,21 +99,21 @@ found_sync = 0; try Scpe_load = load([options.storage_path, char(dataTable.rx_sync_path)]); Scpe_cell = Scpe_load.S; - [~,~,found_sync] = Scpe_cell{2}.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",0); + [~,~,~,found_sync] = Scpe_cell{2}.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1); end if ~found_sync Scpe_sig_raw = load([options.storage_path, char(dataTable.rx_raw_path(1))]); Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",2*fsym); - [~,Scpe_cell,found_sync] =Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1); + [~,Scpe_cell,~,found_sync] =Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1); end if ~found_sync if length(Symbols_mapped.signal) == sum(Symbols_mapped.signal == Symbols.signal) warning('Could not synchronize the received signal with the stored symbols!') else - [~,Scpe_cell,found_sync] =Scpe_sig_raw.tsynch("reference",Symbols_mapped,"fs_ref",dataTable.symbolrate,"debug_plots",0); + [~,Scpe_cell,~,found_sync] =Scpe_sig_raw.tsynch("reference",Symbols_mapped,"fs_ref",dataTable.symbolrate,"debug_plots",0); end if ~found_sync warning('Could not synchronize the received signal with the stored symbols!') @@ -120,7 +121,7 @@ if ~found_sync end record_realizations = min(options.max_occurences,length(Scpe_cell)); -for occ = 4:record_realizations +for occ = 1:record_realizations Scpe_sig = Scpe_cell{occ}; @@ -136,10 +137,10 @@ for occ = 4:record_realizations if duob_mode ~= db_mode.db_encoded - vnle_pf = 0; + vnle_pf = 1; dbtgt = 0; % %%%%% VNLE + DFE %%%% - if 1 + if 0 eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,... @@ -149,6 +150,8 @@ for occ = 4:record_realizations "smoothing_buffer_length",smoothing_buffer_length,... "smoothing_buffer_update",smoothing_buffer_update); + 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); + result = vnle(eq_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0); vnle_package{occ} = result; fprintf("FFE Results: %.2e\n", result.ber_vnle); @@ -196,7 +199,7 @@ for occ = 4:record_realizations % Print header fprintf("==== EQUALIZATION RUN-ID %d | PAM-%d | %.2f GBd ====\n\n", run_id, M, Symbols.fs.*1e-9); -s + % VNLE Results fprintf(">> VNLE Results:\n"); fprintf(" BER %.2e\n", vnle_result.BER); diff --git a/projects/ECOC_2025/dsp_loop_id.m b/projects/ECOC_2025/dsp_standalone_2.m similarity index 98% rename from projects/ECOC_2025/dsp_loop_id.m rename to projects/ECOC_2025/dsp_standalone_2.m index 2d9ab8f..97007ae 100644 --- a/projects/ECOC_2025/dsp_loop_id.m +++ b/projects/ECOC_2025/dsp_standalone_2.m @@ -14,7 +14,7 @@ filterParams.Configurations = struct( ... 'fiber_length', 0, ... 'db_mode', '"no_db"', ... 'interference_attenuation', [], ... - 'interference_path_length', 10, ... + 'interference_path_length', 0, ... 'is_mpi', 1, ... 'pam_level', 4, ... 'wavelength', 1310, ... diff --git a/projects/ECOC_2025/load_signal_standalone.m b/projects/ECOC_2025/load_signal_standalone.m index cb27584..f58c77f 100644 --- a/projects/ECOC_2025/load_signal_standalone.m +++ b/projects/ECOC_2025/load_signal_standalone.m @@ -1,6 +1,5 @@ -run_id = 3966; savePath = 'Z:\2025\ECOC Silas\ecoc_2025\'; databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; @@ -11,37 +10,38 @@ db = DBHandler("type","mysql"); filterParams = db.tables; % filterParams.Configurations = struct('run_id', run_id); filterParams.Configurations = struct( ... - 'symbolrate', [], ... %[224,336,360,390,420,448] + 'symbolrate', 112e9, ... %[224,336,360,390,420,448] 'fiber_length', 0, ... 'db_mode', '"no_db"', ... 'interference_attenuation', 4, ... - 'interference_path_length', 10, ... + 'interference_path_length', 300, ... 'is_mpi', 1, ... - 'pam_level', [], ... + 'pam_level', 4, ... 'wavelength', 1310, ... 'precomp_amp', [], ... 'signal_attenuation', [], ... - 'v_awg', [], ... - 'v_bias', 2.65 ... + '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'}; + 'Configurations.interference_attenuation', 'Measurements.power_mpi_interference'}; [dataTable,sql_query] = db.queryDB(filterParams, selectedFields); [~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices +dataTable.SIR = -7 - round(dataTable.power_mpi_interference); -for i = 1:4%size(dataTable,1) +%% +for i = 1:size(dataTable,1) dataTable_ = dataTable(i,:); % Extract unique configurations for each run_id - + fsym = dataTable_.symbolrate; M = double(dataTable_.pam_level); duob_mode = db_mode.(strrep(char(dataTable_.db_mode),'"','')); - + Tx_signal = load([savePath, char(dataTable_.tx_signal_path)]); Tx_signal = Tx_signal.Digi_sig; @@ -52,17 +52,52 @@ for i = 1:4%size(dataTable,1) Symbols = load([savePath, char(dataTable_.tx_symbols_path)]); Symbols = Symbols.Symbols; - + 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",2*fsym); - [~,Scpe_cell,found_sync,~,shifts] = Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable_.symbolrate,"debug_plots",1); + Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",fsym); + [~,Scpe_cell,found_sync,test,shifts] = Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable_.symbolrate,"debug_plots",0); shifts_mus = shifts./Scpe_sig_resampled.fs .*1e6; - Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0); + Scpe_sig_resampled = Scpe_sig_resampled.normalize("mode","rms"); + % Scpe_sig_resampled.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0); hold on; - xline(shifts_mus,'HandleVisibility','off'); + % xline(shifts_mus,'HandleVisibility','off'); + + shifts = shifts-shifts(1)+1; + sep_sig = NaN(M,length(Scpe_sig_resampled)); + avg_sig = NaN(M,length(Scpe_sig_resampled)); + for j = 1:size(Scpe_cell,1) + + [sep_sig_,avg_sig_]=showLevelScatter(Scpe_cell{j}.resample("fs_out",Symbols.fs),Symbols,"fignum",400); + s = shifts(j); + sep_sig(:,s+1:s+length(sep_sig_)) = sep_sig_; + avg_sig(:,s+1:s+length(avg_sig_)) = avg_sig_; + + end + + disp(num2str(filterParams.Configurations.interference_path_length)); + var(sep_sig,0,2,'omitnan') +%% + xax_in_sec = ((1:length(avg_sig)) / fsym) * 1e6; + figure();hold on; + cols = cbrewer2('Paired',8); + % for p = 1:size(avg_sig,1) + % sc=scatter(xax_in_sec,sep_sig(p,:),1,'.','MarkerEdgeColor',cols((2*p)-1,:),'MarkerEdgeAlpha',0.1); + % end + for p = 1:size(avg_sig,1) + sc=plot(xax_in_sec,avg_sig(p,:),'LineWidth',1,'Color',cols((2*p),:)); + end + + yline(unique(Symbols.signal),'HandleVisibility','off'); + % xline(shifts./ fsym .*1e6,'HandleVisibility','off'); + xlabel('time in $\mu$s'); + ylabel('Normalized Amplitude'); + xlim([0 25]); + ylim([-2 2]); + + drawnow; end \ No newline at end of file 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 eadb53c..3b2696c 100644 --- a/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m +++ b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m @@ -16,7 +16,7 @@ filterParams.Configurations = struct( ... 'wavelength', 1310, ... 'precomp_amp', [], ... 'signal_attenuation', [], ... - 'v_awg', [], ... + 'v_awg', [], ... 'v_bias', [] ... ); @@ -25,7 +25,7 @@ filterParams.Configurations = struct( ... % % 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.smoothing_buffer_update = 224; % filterParams.EqualizerParameters.DCmu = 0; % end @@ -34,220 +34,291 @@ selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Run '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_raw,sql_query] = database.queryDB(filterParams, selectedFields); + +%% + +dataTable_clean = dataTable_raw; +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); + +dataTable_clean(dataTable_clean.BER>0.2,:) = []; %% - - -dataTable_clean = dataTable_raw; -dataTable_clean.SIR = round(-7.5 - dataTable_clean.power_mpi_interference); -dataTable_clean.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level); -dataTable_clean = cleanUpTable(dataTable_clean); - -%% - -dataTable = dataTable_clean; - -figure(26); -plotBoundaries = 1; -plotRealizations = 0; cols = linspecer(8); % Ensure color count matches +figure() +tiledlayout(1, 4, 'TileSpacing', 'compact', 'Padding', 'compact'); +y_here = 0; +figcnt = 0; +for int_len = [0,50,300,1000] + figcnt = figcnt+1; + % figure(int_len+1); + nexttile; + hold on + mode = 4; -% ideal DC tracking -if 0 - dataTable = dataTable(dataTable.dc_buffer_len == 1, :); - dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); - dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); - dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); - dataTable = dataTable(dataTable.DCmu == 0.005, :); - cols = cols(1:1+1,:); - method = 'ideal dc tracking'; - % slow DC tracking -elseif 1 - dataTable = dataTable(dataTable.dc_buffer_len == 224, :); - dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); - dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); - dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); - dataTable = dataTable(dataTable.DCmu == 0.005, :); - cols = cols(2:2+1,:); - method = 'parallelized dc tracking'; - % slow DC smoothing -elseif 0 + for mode = [1,4] + + hold on; + dataTable = dataTable_clean; - dataTable = dataTable(dataTable.dc_buffer_len == 1, :); - dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); - dataTable = dataTable(dataTable.smoothing_buffer_len == 4096, :); - dataTable = dataTable(dataTable.smoothing_buffer_update == 224, :); - dataTable = dataTable(dataTable.DCmu == 0, :); - cols = cols(3:3+1,:); - method = 'dc smoothing'; -elseif 0 - % No compensation method - dataTable = dataTable(dataTable.dc_buffer_len == 1, :); - dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); - dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); - dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); - dataTable = dataTable(dataTable.DCmu == 0, :); - cols = cols(4:4+1,:); - method = 'ffe only'; -end -dataTable(dataTable.eq_id==0,:) = []; -dataTable(dataTable.equalizer_structure~=1,:) = []; + plotBoundaries = 1; + plotRealizations = 1; + cols = linspecer(8); % Ensure color count matches -% dataTable.interference_path_length(dataTable.interference_path_length < 20, :) = 20; + if mode == 1 + % No compensation method + dataTable = dataTable(dataTable.dc_buffer_len == 1, :); + dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); + dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); + dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); + dataTable = dataTable(dataTable.DCmu == 0, :); + cols = cols(1:1+1,:); + method = 'ffe only'; -dataTable = dataTable(dataTable.interference_path_length == 1000, :); + % slow DC smoothing + elseif mode == 2 -% dataTable(dataTable.interference_path_length ~= 50, :) = []; -% dataTable = dataTable(dataTable.interference_path_length < 51, :); + dataTable = dataTable(dataTable.dc_buffer_len == 1, :); + dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); + dataTable = dataTable(dataTable.smoothing_buffer_len == 4096, :); + dataTable = dataTable(dataTable.smoothing_buffer_update == 224, :); + dataTable = dataTable(dataTable.DCmu == 0, :); + + cols = cols(4:4+1,:); + method = 'dc smoothing'; -% dataTable(dataTable.loop_id<200,:) = []; -% Filter by time -filter_by_time = 0; -if filter_by_time - startTime = datetime('2025-04-20 18:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); - stopTime = datetime('2025-04-30 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); - dataTable.date_of_run = datetime(dataTable.date_of_run, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); - dataTable.date_of_processing = datetime(dataTable.date_of_processing, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); - dataTable = dataTable(dataTable.date_of_processing > startTime, :); - dataTable = dataTable(dataTable.date_of_processing < stopTime, :); -end + % slow DC tracking + elseif mode == 3 -% Group by smth -y_var = 'BER'; -x_var = 'SIR'; + dataTable = dataTable(dataTable.dc_buffer_len == 224, :); + dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); + dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); + dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); + dataTable = dataTable(dataTable.DCmu == 0.005, :); + + cols = cols(3:3+1,:); + method = 'parallelized dc tracking'; -loop_var = 'interference_path_length'; -fixedVars = {'equalizer_structure','interference_path_length',x_var}; + elseif mode == 4 -[dataTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var); + % ideal DC tracking + dataTable = dataTable(dataTable.dc_buffer_len == 1, :); + dataTable = dataTable(dataTable.ffe_buffer_len == 1, :); + dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :); + dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :); + dataTable = dataTable(dataTable.DCmu == 0.005, :); + + cols = cols(2:2+1,:); + method = 'ideal dc tracking'; + end -dataTableGrpd_mean = groupIt(fixedVars, dataTable, @mean); -dataTableGrpd_min = groupIt(fixedVars, dataTable, @min); -dataTableGrpd_max = groupIt(fixedVars, dataTable, @max); + dataTable(dataTable.eq_id==0,:) = []; + dataTable(dataTable.equalizer_structure~=1,:) = []; -% Create a new figure -mkr = '.'; -hold on + % Modify values in 'interference_path_length' where the condition is met + dataTable.interference_path_length(dataTable.interference_path_length < 101 & dataTable.interference_path_length > 1) = 50; -unique_loop_var = unique(dataTable.(loop_var)); + dataTable.interference_path_length(dataTable.interference_path_length == 1) = 0; + dataTable = dataTable(dataTable.interference_path_length == int_len, :); -for i = 1:numel(unique_loop_var) - % Prepare filtered data for this loop variable - loopValue = unique_loop_var(i); + dataTable(dataTable.run_id == 3866, :) = []; + dataTable(dataTable.run_id == 3865, :) = []; + dataTable(dataTable.run_id == 3796, :) = []; + dataTable(dataTable.run_id == 3797, :) = []; + dataTable(dataTable.run_id == 3798, :) = []; + dataTable(dataTable.run_id == 4002, :) = []; + dataTable(dataTable.run_id == 4200, :) = []; + dataTable(dataTable.run_id == 4199, :) = []; - loopFiltGrpd = dataTableGrpd_mean.(loop_var) == loopValue; - if ~any(loopFiltGrpd) - continue; % Skip if no data for this loop var - end + % 0 + % 1 + % 10 + % 15 + % 20 + % 50 + % 100 + % 300 + % 1000 - % Extract values - x_values = dataTableGrpd_mean.(x_var)(loopFiltGrpd, :); - y_mean = dataTableGrpd_mean.(y_var)(loopFiltGrpd, :); - y_min = dataTableGrpd_min.(y_var)(loopFiltGrpd, :); - y_max = dataTableGrpd_max.(y_var)(loopFiltGrpd, :); + % dataTable(dataTable.interference_path_length ~= 50, :) = []; + % dataTable = dataTable(dataTable.interference_path_length < 51, :); - % Compute bounds: distance from mean - y_lower = y_mean - y_min; - y_upper = y_max - y_mean; - y_bounds = [y_lower, y_upper]; + % dataTable(dataTable.loop_id<200,:) = []; - % Display name (optional) - try - idx = find(dataTable.(loop_var) == loopValue, 1, 'first'); - % dispname = char(equalizer_structure(dataTable.equalizer_structure(idx))); - dispname = [method]; - dispname = [dispname, '/ ',num2str(unique_loop_var(i)) ,' m']; - % dispname = [dispname,'; ',num2str(unique(dataTable.interference_path_length)),' m']; - dispname = [dispname, '/ PAM ', num2str(filterParams.Configurations.pam_level)]; - dispname = [dispname, '/ ', num2str(filterParams.Configurations.symbolrate.*1e-9),' GBd']; - end + % Filter by time + filter_by_time = 0; + if filter_by_time + startTime = datetime('2025-04-20 18:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); + stopTime = datetime('2025-04-30 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); + dataTable.date_of_run = datetime(dataTable.date_of_run, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); + dataTable.date_of_processing = datetime(dataTable.date_of_processing, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); + dataTable = dataTable(dataTable.date_of_processing > startTime, :); + dataTable = dataTable(dataTable.date_of_processing < stopTime, :); + end - if plotBoundaries - % Plot bounded line - [hl, hp] = boundedline(x_values, y_mean, y_bounds, ... - 'alpha', 'transparency', 0.1, ... - 'cmap', cols(i,:), ... - 'nan', 'fill', ... - 'orientation', 'vert'); - - % % Style the main line: thinnest, dotted, no marker - set(hl, 'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... - 'Color', cols(i,:), 'DisplayName', string(dispname)); + % Group by smth + y_var = 'BER'; + x_var = 'SIR'; - plt = errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 0.1, 'LineStyle', 'none', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off'); - - % Hide patch (shaded area) from legend - set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none'); - - % % Add invisible scatter for DataTips - % plt = scatter(x_values, y_mean, ... - % 'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ... - % 'HandleVisibility', 'off', 'PickableParts', 'all'); - else + loop_var = 'interference_path_length'; + fixedVars = {'equalizer_structure','interference_path_length',x_var}; - plt= plot(x_values,y_mean,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... - 'Color', cols(i,:), 'DisplayName', string(dispname)); - % plt= errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname)); + [dataTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var); + + dataTableGrpd_mean = groupIt(fixedVars, dataTable, @mean); + dataTableGrpd_min = groupIt(fixedVars, dataTable, @min); + dataTableGrpd_max = groupIt(fixedVars, dataTable, @max); + + % Create a new figure + + hold on + + unique_loop_var = unique(dataTable.(loop_var)); + + for i = 1:numel(unique_loop_var) + + % Prepare filtered data for this loop variable + loopValue = unique_loop_var(i); + + loopFiltGrpd = dataTableGrpd_mean.(loop_var) == loopValue; + if ~any(loopFiltGrpd) + continue; % Skip if no data for this loop var + end + + % Extract values + x_values = dataTableGrpd_mean.(x_var)(loopFiltGrpd, :); + y_mean = dataTableGrpd_mean.(y_var)(loopFiltGrpd, :); + y_min = dataTableGrpd_min.(y_var)(loopFiltGrpd, :); + y_max = dataTableGrpd_max.(y_var)(loopFiltGrpd, :); + + % Compute bounds: distance from mean + y_lower = y_mean - y_min; + y_upper = y_max - y_mean; + y_bounds = [y_lower, y_upper]; + + % Display name (optional) + try + idx = find(dataTable.(loop_var) == loopValue, 1, 'first'); + % dispname = char(equalizer_structure(dataTable.equalizer_structure(idx))); + dispname = [method]; + dispname = [dispname, '/ ',num2str(unique_loop_var(i)) ,' m']; + % dispname = [dispname,'; ',num2str(unique(dataTable.interference_path_length)),' m']; + % dispname = [dispname, '/ PAM ', num2str(filterParams.Configurations.pam_level)]; + % dispname = [dispname, '/ ', num2str(filterParams.Configurations.symbolrate.*1e-9),' GBd']; + end + + if plotBoundaries + % Plot bounded line + [hl, hp] = boundedline(x_values, y_mean, y_bounds, ... + 'alpha', 'transparency', 0.1, ... + 'cmap', cols(i,:), ... + 'nan', 'fill', ... + 'orientation', 'vert'); + + % % Style the main line: thinnest, dotted, no marker + set(hl, 'LineWidth', 0.5, 'LineStyle', ':', 'Marker', 'none', ... + 'Color', cols(i,:), 'DisplayName', string(dispname)); + + plt = errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 0.9, 'LineStyle', 'none', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off'); + + % 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 + + % Evaluate the fitted polynomial + x_fit = linspace(min(x_values), max(x_values), 300); % Fine points + y_fit_log = polyval(p, x_fit); % Still in log10 domain + y_fit = 10.^y_fit_log; % Back to BER domain + + + + plot(x_fit,y_fit,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... + 'Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off'); + + % % Add invisible scatter for DataTips + % plt = scatter(x_values, y_mean, ... + % 'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ... + % 'HandleVisibility', 'off', 'PickableParts', 'all'); + else + + plt= plot(x_values,y_mean,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... + 'Color', cols(i,:), 'DisplayName', string(dispname)); + % plt= errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname)); + + end + % Add data tips to the invisible scatter + pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)}; + pair_two = {'Rate', dataTableGrpd_mean.bitrate(loopFiltGrpd, :) * 1e-9}; + pair_three = {'PD in', round(dataTableGrpd_mean.power_pd_in(loopFiltGrpd, :), 2)}; + addDatatips(plt, pair_one, pair_two, pair_three); + + + + % Optionally: outline bounds for better visibility (optional) + % hnew = outlinebounds(hl, hp); + + % Tick marks (x-axis) + + + % Optional: scatter realizations + if plotRealizations + loopFiltSingle = dataTable.(loop_var) == loopValue; + x_single = double(dataTable.(x_var)(loopFiltSingle, :)); + y_single = double(dataTable.(y_var)(loopFiltSingle, :)); + + mkr = '.'; + sc = scatter(x_single+(mode*0.1)-0.2, y_single,15, 'Marker', mkr, 'MarkerEdgeColor', cols(i, :), ... + '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_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)}; + addDatatips(sc, pair_one, pair_two, pair_three); + end + end + + % Label axes and title + legend('Interpreter', 'latex'); + xlabel(x_var); + if ~y_here + ylabel(y_var); + yticklabels = []; + + y_here = 1; + end + if int_len ~= 0 + set(gca, 'YTick', []); + end + % title([x_var, ' vs. ', y_var]); + + if string(y_var) == "BER" + yline(2.2e-4, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); + yline(3.8e-3, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); + yline(2e-2, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); + ylim([1e-5, 0.1]); + end + + xlim([15,35]); + ylim([9e-5 0.1 ]); + xticks([13:2:35]); + + % Enable grid and beautify + grid on; + beautifyBERplot; - end - % Add data tips to the invisible scatter - pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)}; - pair_two = {'Rate', dataTableGrpd_mean.bitrate(loopFiltGrpd, :) * 1e-9}; - pair_three = {'PD in', round(dataTableGrpd_mean.power_pd_in(loopFiltGrpd, :), 2)}; - addDatatips(plt, pair_one, pair_two, pair_three); - - - - % Optionally: outline bounds for better visibility (optional) - % hnew = outlinebounds(hl, hp); - - % Tick marks (x-axis) - xticks(round(unique(x_values),2)); - - % Optional: scatter realizations - if plotRealizations - loopFiltSingle = dataTable.(loop_var) == loopValue; - x_single = double(dataTable.(x_var)(loopFiltSingle, :)); - y_single = double(dataTable.(y_var)(loopFiltSingle, :)); - - sc = scatter(x_single, y_single, 'Marker', mkr, 'MarkerEdgeColor', cols(i, :), ... - '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_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)}; - addDatatips(sc, pair_one, pair_two, pair_three); end end -% Label axes and title -legend('Interpreter', 'latex'); -xlabel(x_var); -ylabel(y_var); -title([x_var, ' vs. ', y_var]); - -if string(y_var) == "BER" - yline(4e-4, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); - yline(3.8e-3, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); - yline(2e-2, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off'); - ylim([1e-5, 0.1]); -end - -xlim([13,35]); - -% Enable grid and beautify -grid on; -beautifyBERplot; - - - function resultTable = groupIt(fixedVars, dataTable, aggregationFunction) % groupIt Groups data in a table based on fixedVars and applies aggregationFunction to numeric data. @@ -463,9 +534,9 @@ for groupIdx = 1:height(groupKeys) continue; end - % Detect outliers in log space + % Detect outliers in log space y_log = log10(y_values); - outlierMask = isoutlier(y_log, 'quartiles'); % or 'median', 'grubbs', etc. + outlierMask = isoutlier(y_log, 'quartiles',1); % or 'median', 'grubbs', etc. % If any outliers found, collect their data if any(outlierMask) diff --git a/projects/HighSpeedExperiment_2024/auswertung MPI/plot_from_database.m b/projects/HighSpeedExperiment_2024/auswertung MPI/plot_from_database.m index 3093427..0aa8010 100644 --- a/projects/HighSpeedExperiment_2024/auswertung MPI/plot_from_database.m +++ b/projects/HighSpeedExperiment_2024/auswertung MPI/plot_from_database.m @@ -1,26 +1,27 @@ basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -database = DBHandler("pathToDB",[basePath,'silas_labor.db']); +database = DBHandler("pathToDB",[basePath,'silas_labor_plain.db'],"type",'sqlite'); filterParams = database.tables; filterParams.Configurations = struct( ... 'bitrate', [], ... %[224,336,360,390,420,448] - 'db_mode', int32(db_mode.no_db), ... + 'db_mode', [], ... 'fiber_length', 1, ... 'interference_attenuation', [], ... 'interference_path_length', [], ... - 'is_mpi', 1, ... + 'is_mpi', 0, ... 'pam_level', 4, ... 'rop_attenuation', 0, ... 'wavelength', 1310 ... ); % filterParams.EqualizerParameters.diff_precode = int32(db_mode.no_db); - filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); + % filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); % filterParams.EqualizerParameters.DCmu = 0.005; selectedFields = {'Configurations.run_id' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.symbolrate' 'Configurations.pam_level' 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.SNR' 'Results.GMI' 'Results.Alpha'}; +% selectedFields = {'Configurations.run_id'}; [dataTable,sql_query] = database.queryDB(filterParams, selectedFields); diff --git a/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m b/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m index e53d7c6..a42bad8 100644 --- a/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m +++ b/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m @@ -1,13 +1,13 @@ basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -database = DBHandler("pathToDB",[basePath,'silas_labor.db']); +database = DBHandler("pathToDB",[basePath,'silas_labor.db'],"type",'sqlite'); filterParams = database.tables; filterParams.Configurations = struct( ... 'bitrate', [], ... %[224,336,360,390,420,448] - 'db_mode', int32(db_mode.db_encoded), ... - 'fiber_length', 10, ... + 'db_mode', [], ... + 'fiber_length', 1, ... 'interference_attenuation', [], ... 'interference_path_length', [], ... 'is_mpi', 0, ... @@ -18,11 +18,11 @@ filterParams.Configurations = struct( ... % filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded); % filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); -filterParams.EqualizerParameters.DCmu = 0.00; +% filterParams.EqualizerParameters.DCmu = 0.00; selectedFields = {'Configurations.run_id' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.symbolrate' 'Configurations.pam_level'... 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' ... - 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' ... + 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'EqualizerParameters.DCmu' 'Measurements.power_pd_in' ... 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'}; [dataTable,sql_query] = database.queryDB(filterParams, selectedFields); diff --git a/projects/HighSpeedExperiment_2024/db_auswertung/realization_vs_ber.m b/projects/HighSpeedExperiment_2024/db_auswertung/realization_vs_ber.m index b941680..786605e 100644 --- a/projects/HighSpeedExperiment_2024/db_auswertung/realization_vs_ber.m +++ b/projects/HighSpeedExperiment_2024/db_auswertung/realization_vs_ber.m @@ -1,11 +1,11 @@ basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -database = DBHandler("pathToDB",[basePath,'silas_labor.db']); +database = DBHandler("pathToDB",[basePath,'silas_labor.db'],"type",'sqlite'); filterParams = database.tables; filterParams.Configurations = struct( ... - 'bitrate', 450e9, ... %[224,336,360,390,420,448] + 'bitrate', 420e9, ... %[224,336,360,390,420,448] 'db_mode', int32(db_mode.no_db), ... 'fiber_length', 10, ... 'interference_attenuation', [], ... diff --git a/projects/Job_Processing/run_offline_dsp.m b/projects/Job_Processing/run_offline_dsp.m new file mode 100644 index 0000000..2544680 --- /dev/null +++ b/projects/Job_Processing/run_offline_dsp.m @@ -0,0 +1,117 @@ +% === SETTINGS === + +dsp_options.append_to_db = 1; +dsp_options.max_occurences = 15; +dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +dsp_options.database_name = 'silas_labor_newdsp_newstructure.db'; +dsp_options.storage_path = 'Z:\2024\sioe_labor\'; +dsp_options.parameters = struct(); +dsp_options.parameters.mu_dc = [0.005]; + +% === Get Run ID's === +db = DBHandler("pathToDB", [dsp_options.database_path, dsp_options.database_name], "type", "sqlite"); +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 5108); +fp.where('Runs', 'is_mpi','EQUALS', 0); +% fp.where('Runs', 'fiber_length','EQUALS', 10); +fp.where('Runs', 'wavelength','EQUALS', 1310); +fp.where('Runs', 'db_mode','EQUALS', 1); +fp.where('Runs', 'rop_attenuation','EQUALS', 0); +fp.where('Runs', 'pam_level','EQUALS', 4); +% fp.where('Runs', 'bitrate','EQUALS', 360e9); +% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); +[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); + +% === Initialize DataStorage === +wh = DataStorage(dsp_options.parameters); +wh.addStorage("ffe_package"); +wh.addStorage("mlse_package"); +wh.addStorage("vnle_package"); +wh.addStorage("dbtgt_package"); +wh.addStorage("dbenc_package"); + +% === RUN IT === + +% [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); +% wh.getStoValue('ffe_package',0.005); +% wh.getStoValue('mlse_package',0.005); + +[dataTable,~] = db.queryDB(fp, [db.getTableFieldNames('Runs');db.getTableFieldNames('Results');db.getTableFieldNames('Equalizer')]); + +dataTable = cleanUpTable(dataTable); + +% === Look at it === +y_var = 'BER_precoded'; +x_var = 'bitrate'; +fixedVars = {'equalizer_structure', x_var}; + +[dataTableClean, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var); + +% --- Group and aggregate --- +dataTableGrpd_mean = groupIt(fixedVars, dataTableClean, @mean); +dataTableGrpd_min = groupIt(fixedVars, dataTableClean, @min); +dataTableGrpd_max = groupIt(fixedVars, dataTableClean, @max); + +% Choose a color map +cols = linspecer(numel(unique(dataTableGrpd_mean.equalizer_structure))); + +figure; +hold on; + +% Get unique equalizer structures for grouping +unique_eq = unique(dataTableGrpd_mean.equalizer_structure); + +for i = 1:numel(unique_eq) + eq_val = unique_eq(i); + + % Filter grouped data for this equalizer structure + filt = dataTableGrpd_mean.equalizer_structure == eq_val; + + x = dataTableGrpd_mean.(x_var)(filt); + y_mean = dataTableGrpd_mean.(y_var)(filt); + y_min = dataTableGrpd_min.(y_var)(filt); + y_max = dataTableGrpd_max.(y_var)(filt); + + % Bounds for boundedline (distance from mean) + y_lower = y_mean - y_min; + y_upper = y_max - y_mean; + y_bounds = [y_lower, y_upper]; + + % --- Bounded line (mean ± min/max) --- + if exist('boundedline', 'file') + [hl, hp] = boundedline(x, y_mean, y_bounds, ... + 'alpha', 'transparency', 0.1, ... + 'cmap', cols(i,:), ... + 'nan', 'fill', ... + 'orientation', 'vert'); + set(hl, 'LineWidth', 1.2, 'DisplayName', sprintf('Eq %s', eq_val)); + set(hp, 'HandleVisibility', 'off'); + else + % If boundedline is not available, use errorbar + errorbar(x, y_mean, y_lower, y_upper, ... + 'o-', 'Color', cols(i,:), 'LineWidth', 1.2, ... + 'DisplayName', sprintf('Eq %d', eq_val),'HandleVisibility', 'off'); + end + + % --- Normal line (mean only) --- + plot(x, y_mean, '-', 'Color', cols(i,:), 'LineWidth', 1.5, ... + 'DisplayName', sprintf('Mean Eq %s', eq_val),'HandleVisibility', 'off'); + + % --- Scatter plot for individual points (from original data) --- + % Filter original data for this group + orig_filt = dataTableClean.equalizer_structure == eq_val; + x_scatter = dataTableClean.(x_var)(orig_filt); + y_scatter = dataTableClean.(y_var)(orig_filt); + + scatter(x_scatter, y_scatter, 10,cols(i,:), 'filled', ... + 'MarkerFaceAlpha', 0.5, 'DisplayName', sprintf('Scatter Eq %s', eq_val),'HandleVisibility', 'off'); +end + +yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); +set(gca, 'YScale', 'log'); % BER is usually plotted log-scale +xlabel(x_var, 'Interpreter', 'none'); +ylabel(y_var, 'Interpreter', 'none'); +legend('show', 'Location', 'best'); +grid on; +title(sprintf('%s vs. %s', y_var, x_var), 'Interpreter', 'none'); +hold off; \ No newline at end of file From 2cff29f2391e70e38cca9e9cb47d87cf39fbdfdb Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 9 Jul 2025 10:50:53 +0200 Subject: [PATCH 03/30] Many changes towards simulation of JLT and once again the evaluation of the Highspeed data from Lab experiments 2024 --- Classes/01_transmit/PAMmapper.m | 5 +- Classes/01_transmit/PAMsource.m | 2 +- Classes/04_DSP/Equalizer/FFE.m | 128 ++++- .../Equalizer/FFE_DCremoval_adaptive_mu.m | 127 ++++- Classes/04_DSP/Equalizer/FFE_DFE.m | 7 +- Classes/04_DSP/Equalizer/Postfilter.m | 7 +- Classes/04_DSP/Sequence Detection/MLSE.m | 534 ++++++++---------- Classes/DataBaseHandler/DBHandler.m | 50 +- Classes/DataBaseHandler/Metricstruct.m | 68 ++- Classes/Warehouse_class/classes/DataStorage.m | 2 +- Datatypes/adaption_method.m | 9 + Functions/EQ_structures/dsp_runid.m | 196 +++++-- Functions/EQ_structures/duobinary_signaling.m | 13 +- Functions/EQ_structures/duobinary_target.m | 19 +- Functions/EQ_structures/ffe.m | 64 ++- .../EQ_structures/vnle_postfilter_mlse.m | 56 +- Functions/EQ_visuals/showEQNoisePSD.m | 3 + Functions/EQ_visuals/showEQcoefficients.m | 4 +- Functions/EQ_visuals/showEQfilter.m | 27 +- Functions/EQ_visuals/showLevelHistogram.m | 7 +- Functions/EQ_visuals/showLevelScatter.m | 4 +- ...alData.m => loadAndSyncSignalDataFromDb.m} | 36 +- Functions/Job_Processing/submitJobs.m | 14 +- Functions/Metrics/calc_snr.m | 4 +- .../Theory/analyze_moving_average_filter.m | 64 +++ .../auswertung_algorithms/mpi_dsp_debug.m | 60 ++ .../auswertung_algorithms/run_offline_dsp.m | 118 ++++ .../Auswertung_JLT/run_dsp_from_db.m | 224 ++++++++ projects/IMDD_base_system/imdd_model.m | 1 + projects/IMDD_base_system/simulation_bwl.m | 227 ++++++++ projects/IMDD_base_system/simulation_bwl_2.m | 144 +++++ projects/IMDD_base_system/tx_simulation.m | 2 +- projects/Job_Processing/run_offline_dsp.m | 2 +- .../Job_Processing/run_offline_dsp_script.m | 116 ++++ test/duobinary_minimal_example.m | 79 ++- 35 files changed, 1874 insertions(+), 549 deletions(-) create mode 100644 Datatypes/adaption_method.m rename Functions/Job_Processing/{loadSignalData.m => loadAndSyncSignalDataFromDb.m} (71%) create mode 100644 Functions/Theory/analyze_moving_average_filter.m create mode 100644 projects/ECOC_2025/auswertung_algorithms/mpi_dsp_debug.m create mode 100644 projects/ECOC_2025/auswertung_algorithms/run_offline_dsp.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m create mode 100644 projects/IMDD_base_system/simulation_bwl.m create mode 100644 projects/IMDD_base_system/simulation_bwl_2.m create mode 100644 projects/Job_Processing/run_offline_dsp_script.m diff --git a/Classes/01_transmit/PAMmapper.m b/Classes/01_transmit/PAMmapper.m index 6ba8558..cf4df17 100644 --- a/Classes/01_transmit/PAMmapper.m +++ b/Classes/01_transmit/PAMmapper.m @@ -45,7 +45,7 @@ classdef PAMmapper signal_in = signal_in.logbookentry(lbdesc,obj); out = signal_in; else - out = signal_in; + out = obj.map_(signal_in); end end @@ -328,8 +328,9 @@ classdef PAMmapper if ~obj.eth_style - data_in = data_in/(sqrt(mean(abs(data_in).^2))); + % data_in = data_in/(sqrt(mean(abs(data_in).^2))); data_in = data_in*sqrt(10); + %data_in = data_in*sqrt((5^2 + 3^2 + 1^2 + 5^2 + 3^2 + 1^2)/6); if size(data_in,2) > 1 data_in = data_in.'; diff --git a/Classes/01_transmit/PAMsource.m b/Classes/01_transmit/PAMsource.m index f1f8e5a..dd77f89 100644 --- a/Classes/01_transmit/PAMsource.m +++ b/Classes/01_transmit/PAMsource.m @@ -164,7 +164,7 @@ classdef PAMsource %%%%% Pulse-forming %%%%%% if obj.applypulseform %%% MY CODE - if 0 + if 1 digi_sig = obj.pulseformer.process(symbols); else diff --git a/Classes/04_DSP/Equalizer/FFE.m b/Classes/04_DSP/Equalizer/FFE.m index 54d0b57..5920e89 100644 --- a/Classes/04_DSP/Equalizer/FFE.m +++ b/Classes/04_DSP/Equalizer/FFE.m @@ -3,24 +3,34 @@ classdef FFE < handle % 1) Training mode (stable performance when you use NLMS) % 2) Decision directed mode - % Eq = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0); + %LMS: mu in order of 0.001 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 + debug_struct len_tr mu_tr epochs_tr - mu_dd + adaption_technique % nlms, lms, rls + dd_mode % 1 or 0 to set DD-mode on or off + mu_dd %weight update in dd mode epochs_dd - constellation + P % covariance matrix of rls - decide + constellation % symbol constellation + + decide %wether to return the (hard) decisions or the result after FFE (soft) end methods @@ -34,6 +44,8 @@ classdef FFE < handle options.mu_tr = 0; options.epochs_tr = 5; + options.adaption_technique adaption_method = adaption_method.lms; + options.dd_mode = 1; options.mu_dd = 1e-5; options.epochs_dd = 5; @@ -59,10 +71,15 @@ classdef FFE < handle obj.constellation = unique(D.signal); + + delta = 0.05; + obj.P = (1/delta) * eye(obj.order); + % Training Mode training = 1; showviz = 0; obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training,showviz); + obj.e_tr = obj.e; % Decision Directed Mode n = X.length; @@ -71,7 +88,7 @@ classdef FFE < handle [signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,n,training,showviz); % Output Signal - if obj.decide + if obj.decide X.signal = decision; else X.signal = signal; @@ -83,24 +100,25 @@ classdef FFE < handle Noi = X; Noi = X - D; - + end - function [y,d_hat] = equalize(obj,x,d,mio,epochs,N,training,showviz) + function [y,d_hat] = equalize(obj,x,d,mu,epochs,N,training,showviz) arguments - obj - x - d - mio - epochs - N - training + obj + x + d + mu + epochs + N + training showviz end x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)]; + lambda = mu; if training mask = ones(obj.order,1); @@ -111,8 +129,17 @@ classdef FFE < handle end mask = ones(obj.order,1); + maincursor_pos=ceil(length(obj.e)/2); + always_ideal_decision = 0; + save_debug = 1; + grad =0; + weight = 0; + update = 0; + + if mu == 0 || (~obj.dd_mode && ~training) + epochs = 1; + end - for epoch = 1 : epochs symbol = 0; for sample = 1 : obj.sps : N @@ -124,30 +151,75 @@ classdef FFE < handle y(symbol,1) = (obj.e.*mask).' * U; % Calculating output of LMS __ * | if training - d_hat(symbol,1) = d(symbol); + d_hat(symbol,1) = d(symbol); else - [~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point - d_hat(symbol,1) = obj.constellation(symbol_idx); + if ~always_ideal_decision + [~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point + d_hat(symbol,1) = obj.constellation(symbol_idx); + else + d_hat(symbol,1) = d(symbol); + end end - err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error + % err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error + err(symbol) = d_hat(symbol) - y(symbol); % Instantaneous error true_err(symbol) = y(symbol) - d(symbol); % Instantaneous error - if mio ~= 0 - obj.e = obj.e - (mio * err(symbol) * U) ; % Weight update rule of LMS - else - normalizationfactor = (U.' * U); - obj.e = obj.e - err(symbol) * U / normalizationfactor; % Weight update rule of NLMS + if training || obj.dd_mode + switch obj.adaption_technique + + case adaption_method.lms + + % mu used as update weight (suggestion: 0.001) + weight = mu; + grad = err(symbol) * U; + update = grad * weight; + obj.e = obj.e + update; + + case adaption_method.nlms + + % mu used as update weight (suggestion: 0.01-0.05; bit higher during tr) + normU = ((U.'*U)) + eps; + weight = mu / normU; + grad = err(symbol) * U; + update = grad * weight; + obj.e = obj.e + update; + + case adaption_method.rls + + + % RLS‐Gain: + denom = lambda + U.' * obj.P * U; + k = (obj.P * U) / denom; + + % Gewichtsupdate: + update = k * err(symbol); + obj.e = obj.e + update; + + % P-Matrix‐Update: + obj.P = (1/lambda) * (obj.P - k * (U.' * obj.P)); + + + end end - obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error + + if save_debug + obj.debug_struct.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error + if training + obj.debug_struct.error_tr(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error + obj.debug_struct.update_tr(epoch,symbol) = update.'*update ./ rms(obj.e); + end + obj.debug_struct.main_cursor(epoch,symbol) = abs(obj.e(maincursor_pos)); + obj.debug_struct.mu_nlms(epoch,symbol) = weight; + obj.debug_struct.update_gradient(epoch,symbol) = grad.'*grad; + obj.debug_struct.update(epoch,symbol) = update.'*update ./ rms(obj.e); + end end end end - end -end - +end \ No newline at end of file diff --git a/Classes/04_DSP/Equalizer/FFE_DCremoval_adaptive_mu.m b/Classes/04_DSP/Equalizer/FFE_DCremoval_adaptive_mu.m index c6b1a43..9265652 100644 --- a/Classes/04_DSP/Equalizer/FFE_DCremoval_adaptive_mu.m +++ b/Classes/04_DSP/Equalizer/FFE_DCremoval_adaptive_mu.m @@ -9,6 +9,7 @@ classdef FFE_DCremoval_adaptive_mu < handle sps % usually 2 order e + e_tr error len_tr @@ -21,6 +22,8 @@ classdef FFE_DCremoval_adaptive_mu < handle mu_dc dc_buffer_len + adaptive_mu_mode + ffe_buffer_len smoothing_buffer_length @@ -50,6 +53,8 @@ classdef FFE_DCremoval_adaptive_mu < handle options.ffe_buffer_len = 1; + options.adaptive_mu_mode = 1; + options.smoothing_buffer_length = 0; options.smoothing_buffer_update = 0; options.decide = false; @@ -90,6 +95,7 @@ classdef FFE_DCremoval_adaptive_mu < handle % Training Mode training = 1; obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training); + obj.e_tr = obj.e; % Decision Directed Mode N = X.length; @@ -124,6 +130,10 @@ classdef FFE_DCremoval_adaptive_mu < handle training % boolean flag: true->training mode, false->DD mode end + if isempty(obj.e) + obj.e = zeros(obj.order,1); + end + % Zero-padding for filter memory x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)]; @@ -140,7 +150,7 @@ classdef FFE_DCremoval_adaptive_mu < handle err_prev = 0; % previous error sample for VSS correlation gamma_dc = 1e-6; % meta step-size for DC VSS mu_min = 1e-6; % lower bound for mu_dc - mu_max = 1e-1; % upper bound for mu_dc + mu_max = 3e-1; % upper bound for mu_dc % DC removal buffer L = obj.dc_buffer_len; % buffer length @@ -152,7 +162,6 @@ classdef FFE_DCremoval_adaptive_mu < handle if ~training % each column holds one past gradient of length obj.order grad_buf = NaN(obj.order, L_grad); - end smth_buffer = zeros(1, obj.smoothing_buffer_length); @@ -187,7 +196,11 @@ classdef FFE_DCremoval_adaptive_mu < handle %-- 3) error e_val = y(s) - d_hat(s); - err(s,idx) = e_val; + if epoch == epochs + + err(s,idx) = e_val; + true_err(s,idx) = y(s) - d(s); + end %-- 4) tap-weight update: training immediate, DD buffered if training @@ -199,7 +212,7 @@ classdef FFE_DCremoval_adaptive_mu < handle obj.e = obj.e - e_val * U / normU; end else - if 1 + if 0 % buffer gradient if mu_lms ~= 0 grad = e_val * U; @@ -226,22 +239,40 @@ classdef FFE_DCremoval_adaptive_mu < handle %-- 5) DC adaptation if obj.mu_dc ~= 0 - % VSS for mu_dc - delta_mu = gamma_dc * e_val * err_prev * (U.'*U); - obj.mu_dc = min(max(obj.mu_dc + delta_mu, mu_min), mu_max); - err_prev = e_val; + if obj.adaptive_mu_mode + + % VSS for mu_dc + delta_mu = gamma_dc * e_val * err_prev * (U.'*U); + obj.mu_dc = min(max(obj.mu_dc + delta_mu, mu_min), mu_max); + err_prev = e_val; + + % DC buffer update & periodic estimate + P_err = alpha*P_err + (1-alpha)*e_val^2; + mu_dc_norm = obj.mu_dc / (P_err + eps); + + else + + % DC buffer update & periodic estimate + % P_err = alpha*P_err + (1-alpha)*e_val^2; + % mu_dc_norm = obj.mu_dc / (P_err + eps); + + mu_dc_norm = obj.mu_dc; + + end - % DC buffer update & periodic estimate - P_err = alpha*P_err + (1-alpha)*e_val^2; - mu_dc_norm = obj.mu_dc / (P_err + eps); e_dc_buf = circshift(e_dc_buf, 1); e_dc_buf(1) = e_dc_est - mu_dc_norm * e_val; - + if mod(s, L) == 0 e_dc_est = median(e_dc_buf, 'omitnan'); end + P_err_save(s) = P_err; + % Pcorr_save(s) = e_val * err_prev; + Ucorr_save(s) = (U.'*U); + mu_dc_save(s) = mu_dc_norm; e_dc_save(s) = e_dc_est; + end % store instantaneous squared error @@ -250,23 +281,71 @@ classdef FFE_DCremoval_adaptive_mu < handle end % Optional plotting in DD mode (uncomment if needed) - if ~training - figure(342);clf + if 0%~training + + constellation = unique(d); + lvlcol = cbrewer2('Paired', numel(constellation)*2); + lvlcol = lvlcol(2:2:end, :); + + true_err(true_err==0) = NaN; + true_errmoverr = movsum(true_err, 4096, 'omitnan'); + true_errmoverr = true_errmoverr./rms(true_errmoverr); + + moverr = movsum(err, [100,100], 'omitnan'); + moverr = moverr./rms(moverr); + + figure(500); clf hold on - scatter(1:numSymbols, err + obj.constellation', '.', 'SizeData', 1); - - yline(obj.constellation); - + % 1st subplot: true_errmoverr + % subplot(2,2,1); hold on + % for k = 1:4 + % scatter(1:numSymbols, true_errmoverr(:,k), 1, lvlcol(k,:), '.'); + % end + + % scatter(1:numSymbols, Ucorr_save./rms(Ucorr_save), 1, lvlcol(1,:), '.','DisplayName','Ucorr_save'); + % scatter(1:numSymbols, Pcorr_save./rms(Pcorr_save), 1, lvlcol(1,:), '.','DisplayName','P_corr'); + % scatter(1:numSymbols, P_err_save, 1, lvlcol(1,:), '.','DisplayName','P_err'); + % scatter(1:numSymbols, mu_dc_save, 1, lvlcol(2,:), '.','DisplayName','adapted value of $\mu_{DC}$'); + % scatter(1:numSymbols, sum(moverr,2,'omitnan'), 1, lvlcol(1,:), '.','DisplayName','Mov Error $\hat{d}$ - x over all levels'); + scatter(1:numSymbols, sum(e_dc_save,2,'omitnan'), 1, lvlcol(2,:), '.','DisplayName','Est. Error that is subtracted'); + title('Moving Sum Error'); + hold off + legend + + % 2nd subplot: moverr + subplot(2,2,2); hold on + for k = 1:4 + scatter(1:numSymbols, moverr(:,k), 1, lvlcol(k,:), '.'); + end + title('Moving Sum Error'); + hold off + legend + + % 3rd subplot: err + subplot(2,2,3); hold on + for k = 1:4 + scatter(1:numSymbols, err(:,k), 1, lvlcol(k,:), '.'); + end + title('Error'); + hold off + legend + + % 4th subplot: err + obj.constellation' + subplot(2,2,4); hold on + for k = 1:4 + scatter(1:numSymbols, err(:,k) + obj.constellation(k), 1, lvlcol(k,:), '.'); + end + yline(obj.constellation, '--k'); + title('Error + Constellation'); + hold off + legend + + sgtitle('Error Analysis Subplots'); + end end - function mu = update_mu() - - - - end - end end diff --git a/Classes/04_DSP/Equalizer/FFE_DFE.m b/Classes/04_DSP/Equalizer/FFE_DFE.m index 0841f8d..e896500 100644 --- a/Classes/04_DSP/Equalizer/FFE_DFE.m +++ b/Classes/04_DSP/Equalizer/FFE_DFE.m @@ -60,7 +60,7 @@ classdef FFE_DFE < handle end - function [X] = process(obj, X, D) + function [X,Noi] = process(obj, X, D) % actual processing of the signal (steps 1. - 3.) % 1 normalize RMS @@ -88,6 +88,9 @@ classdef FFE_DFE < handle X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym lbdesc = [num2str(obj.ffe_order),' tap FFE']; X = X.logbookentry(lbdesc); % append to logbook + + Noi = X; + Noi = X - D; end @@ -185,8 +188,6 @@ classdef FFE_DFE < handle obj.e = coeff(1:obj.ffe_order); obj.b = coeff(obj.ffe_order+1:end); - - end end diff --git a/Classes/04_DSP/Equalizer/Postfilter.m b/Classes/04_DSP/Equalizer/Postfilter.m index 8d09ef1..659801e 100644 --- a/Classes/04_DSP/Equalizer/Postfilter.m +++ b/Classes/04_DSP/Equalizer/Postfilter.m @@ -31,7 +31,7 @@ classdef Postfilter < handle end - function signalclass_out = process(obj,signalclass_in,noiseclass_in,options) + function [signalclass_out,noiseclass_out] = process(obj,signalclass_in,noiseclass_in,options) arguments obj @@ -55,7 +55,8 @@ classdef Postfilter < handle else end - + + noiseclass_in = noiseclass_in.filter(obj.coefficients,1); signalclass_in = signalclass_in.filter(obj.coefficients,1); % append to logbook @@ -64,7 +65,7 @@ classdef Postfilter < handle % write to output signalclass_out = signalclass_in; - + noiseclass_out = noiseclass_in; end function showFilter(obj,options) diff --git a/Classes/04_DSP/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index 9576524..c453e18 100644 --- a/Classes/04_DSP/Sequence Detection/MLSE.m +++ b/Classes/04_DSP/Sequence Detection/MLSE.m @@ -34,22 +34,28 @@ classdef MLSE < handle end - function [signalclass_hd,signalclass_sd] = process(obj,signalclass,ref_symbolclass) + function [signalclass_hd,LLR,GMI] = process(obj,signalclass,ref_symbolclass) data_in = signalclass.signal; + shape_in = size(data_in); data_ref = ref_symbolclass.signal; - [data_out_hd,data_out_sd] = obj.process_(data_in,data_ref); + [data_out_hd,LLR,GMI] = obj.process_(data_in,data_ref); + try + data_out_hd = reshape(data_out_hd,shape_in(1),shape_in(2)); + catch + warning('output reshaping failed after MLSE'); + end signalclass_hd = signalclass; signalclass_hd.signal = data_out_hd; - signalclass_sd = signalclass; - signalclass_sd.signal = data_out_sd; + % signalclass_sd = signalclass; + % signalclass_sd.signal = data_out_sd; end - function [VITERBI_ESTIMATION_SYMBOLS,soft_decisions] = process_(obj,data_in,data_ref) + function [VITERBI_ESTIMATION_SYMBOLS,LLR_maxlogmap,GMI] = process_(obj,data_in,data_ref) % remove unnecessary zeros at start of impulse response to keep @@ -70,35 +76,33 @@ classdef MLSE < handle %%%% Separate the equalized signal into the respective levels based on the actually transmitted level constellation = unique(data_ref); decisionLevels = (constellation(1:end-1) + constellation(2:end)) / 2; - tx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(data_ref); + N = length(data_in); + tx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(data_ref); - % impulse respnse i.e. [0.5, 1.0000] - obj.DIR = flip(obj.DIR); - - % RMS normalization of input data - data_in = data_in ./ rms(data_in); + % impulse respnse to remove from signal + obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000 % 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 - % Save first and last symbol of each state - first_sym = combs(:,1); - last_sym = combs(:,end); states = sum(combs,2); + 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 - noise_free_received = zeros(length(states),length(states)); + noise_free_received = zeros(nStates,nStates); count_row = 1; count_col = 1; - for l1 = 1:length(states) - for l2 = 1:length(states) + for l1 = 1:nStates + for l2 = 1:nStates if sum(combs(l2,2:end) == combs(l1,1:end-1)) == size(combs,2)-1 noise_free_received(count_row,count_col) = sum(combs(l2,:).*obj.DIR(end:-1:2)) + last_sym(l1)*obj.DIR(1); else @@ -110,8 +114,11 @@ classdef MLSE < handle count_row = 1; end - % match amplitude levels of input signal to those of the calculated ideal symbols - % i.e. match the rms values of data_in to noise_free_received + % first: RMS normalization of input data (rms==1) + data_in = data_in ./ rms(data_in); + + % then, match amplitude levels of input signal to those of the calculated ideal symbols + % i.e. match the rms values of data_in to noise_free_received (rms=1.xx) if isreal(data_in) if obj.M == round(obj.M) data_in = data_in * rms(noise_free_received(noise_free_received ~= inf),'all','omitnan'); @@ -119,338 +126,299 @@ classdef MLSE < handle end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%% FORWARD PASS %%%%% + %%%%% FORWARD PASS (VITERBI -Alpha's) %%%%% % Initialize the output vector - pm = zeros(length(states),length(states)); - bm_fw = zeros(length(states),length(states),length(data_in)); + 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 ; + 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 = 1:length(data_in) + for n = 2:length(data_in) - - bm = abs(data_in(n) - noise_free_received).^2; + bm = -(data_in(n) - noise_free_received).^2 ; pm = pm + bm; - [pm_survivor_fw(:,n),pm_survivor_fw_idx(:,n)] = min(pm,[],2); % choose lowest path metric as new state - pm = repmat(pm_survivor_fw(:,n).',length(states),1); % update pm (chosen state to 2nd dimension -> FROM state) + [alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as 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 - best_fw_path = NaN(1,length(data_in)+1); + viterbi_path = NaN(1,length(data_in)); % find ideal trellis path by going through the trellis backwards - [~,best_fw_path(length(data_in)+1)] = min(pm_survivor_fw(:,length(data_in))); + [~,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 + + VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path); - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%% BACKWARD PASS %%%%% + %%%%% BACKWARD PASS (Beta's) %%%%% % Initialize the output vector - pm = zeros(length(states),length(states)); - pm_survivor_bw = zeros(length(states),length(data_in)+1); - pm_survivor_bw_idx = zeros(length(states),length(data_in)+1); - bm_bw = zeros(length(states),length(states),length(data_in)+1); + 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 + for h = length(data_in)-1:-1:1 - best_fw_path(h) = pm_survivor_fw_idx(best_fw_path(h+1),h); - - bm = abs(data_in(h) - noise_free_received).^2; + bm = -(data_in(h+1) - noise_free_received).^2 ; pm = pm + bm.'; - [pm_survivor_bw(:,h),pm_survivor_bw_idx(:,h)] = min(pm,[],2); % choose lowest path metric as new state - pm = repmat(pm_survivor_bw(:,h).',length(states),1); % update pm (chosen state to 2nd dimension -> FROM state) + [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 - VITERBI_ESTIMATION_IDX(1:length(data_in)) = first_sym(best_fw_path(2:end)); - VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = constellation(best_fw_path(2:end)); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% FORWARD PASS PAM 2,4,8 (Combine Alpha and Beta to yield LLP's) %%%%% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%% FORWARD PASS %%%%% - - %calc the log probabilities (llp's) + %calc the log probabilities (llp's) - for k = 2:length(data_in) - - fsm = repmat(pm_survivor_fw(:,k-1)',[length(states),1]); %pm_survivor_fw size: length(states)xlength(sequence) - bm = bm_fw(:,:,k); %bm_fw size: length(states)xlength(states)xlength(sequence) - bsm = repmat(pm_survivor_bw(:,k)',[length(states),1]); %pm_survivor_bw size: length(states)xlength(sequence)+1 + for k = 1:length(data_in) - llp(:,k) = min(fsm+bm+bsm,[],2); - - end + if k == 1 - % Compute soft output PAM4 stream from the metric_sym - soft_output = zeros(length(data_in),1); % Expected symbol value per stage - symbol_prob = zeros(length(data_in), length(states)); % Store full probability distribution (optional) - llp = llp'; - for n = 1:length(data_in) - metrics = llp(n, :); % A posteriori metric for each PAM4 candidate - % For numerical stability, subtract the maximum metric before exponentiating - maxMetric = min(metrics); - expMetrics = exp(metrics - maxMetric); - probs = expMetrics / sum(expMetrics); % Normalize to get probabilities - symbol_prob(n, :) = probs; % (Optional) store distribution for analysis - % Compute the soft output as the expected value of the PAM4 symbols - soft_output(n) = sum(probs .* constellation'); - end + alpha_ = repmat(alpha(:,k)',[nStates,1])'; + beta_ = beta(:,k); - % Number of symbols and bits per symbol - num_symbols = constellation; - num_bits = 2; % 2 bits per symbol + LLP(:,k) = max(alpha_ + beta_,[],2); - bit_mapping = PAMmapper(4,0,"eth_style",1).showBitMapping; % Each row corresponds to the symbol above + else - % Initialize LLR storage - llr = zeros(num_bits, length(data_in)); + alpha_ = repmat(alpha(:,k-1)',[nStates,1])'; + gamma_ = bm_fw(:,:,k)'; + beta_ = beta(:,k); - % Compute bit-wise LLRs - for bit_idx = 1:num_bits - % Find indices where bit is 0 and where it is 1 - idx_bit_0 = find(bit_mapping(:,bit_idx) == 0); - idx_bit_1 = find(bit_mapping(:,bit_idx) == 1); - - % Sum over log-probabilities (Max-Log approximation: using min instead of sum) - llr(:,bit_idx) = min(llp(:,idx_bit_1), [], 1) - min(llp(:,idx_bit_0), [], 1); - - end - - % Convert LLR values to a hard-decision bit stream - bit_stream = llr < 0; - [~,~,ber_llr,~] = calc_ber(bit_stream',tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('LLR BER : %.2e \n',ber_llr); - - % directly decide based on lowest LLP index - [~,llp_based_state_seq]=min(llp); - LLP_EST(1:length(data_in)) = constellation(llp_based_state_seq); - rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(LLP_EST'); - [~,~,ber_llp,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('LLP BER : %.2e \n',ber_llp); - % [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % fprintf('LLP BER +1: %.2e \n',ber_llp); - % [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,-1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % fprintf('LLP BER -1: %.2e \n',ber_llp); - - %%%% DECIDE based on Viterbi traceback - rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(VITERBI_ESTIMATION_SYMBOLS'); - [~,~,ber_viterbi,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('Viterbi BER: %.2e \n',ber_viterbi); - % [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % fprintf('Viterbi BER: %.2e \n',ber_viterbi); - - % directly decide based on the FW path metrics - [~,fw_direct_state_seq]=min(pm_survivor_fw); - FW_EST(1:length(data_in)) = constellation(fw_direct_state_seq); - rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(FW_EST'); - [~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('FW BER: %.2e \n',ber_fw); - - % directly decide based on the BW path metrics - [~,bw_direct_state_seq]=min(pm_survivor_bw); - BW_EST(1:length(data_in)) = constellation(bw_direct_state_seq(2:end)); - rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(BW_EST'); - [~,~,ber_bw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('BW BER: %.2e \n',ber_bw); - % [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % fprintf('BW BER: %.2e \n',ber_viterbi); - % [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,-1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % fprintf('BW BER: %.2e \n',ber_viterbi); - - PAMmapper(4,0,"eth_style",1).showBitMapping - - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - tx_symbolpos = zeros(numel(constellation),length(data_ref)); - est_symbolpos = zeros(numel(constellation),length(data_ref)); - for lvl = 1:numel(constellation) - tx_symbolpos(lvl,data_ref==constellation(lvl)) = 1; - est_symbolpos(lvl,VITERBI_ESTIMATION_IDX==first_sym(lvl)) = 1; - est_symbolpos_llm(lvl,llp_based_state_seq==lvl) = 1; - end - - est_symbolpos= logical(est_symbolpos); - tx_symbolpos = logical(tx_symbolpos); - est_symbolpos_llm = logical(est_symbolpos_llm); - - figure(299);clf;hold on; - llp_filt = NaN(length(states),length(data_in)); - llp_ = llp - min(llp,[],1); - for c = 2%:numel(constellation) - for c2 = 1:3%numel(constellation) - - idx = est_symbolpos_llm(c,:); - llp_filt(c2,idx) = (llp(c2,idx)); - - plotidx = 2:200000; - scatter(plotidx,llp_filt(c2,plotidx),1,'o','LineWidth',1,'DisplayName',sprintf('LLP of Symbol %d | %d was transmitted',c2,c)); + LLP(:,k) = max(alpha_ + gamma_,[],1) + beta_'; + end end - end - % Assume llp is a 4 x N matrix (4 PAM-4 symbols, N time steps) - [numSymbols, numTimeSteps] = size(llp); - P_symbols = zeros(numSymbols, numTimeSteps); % To hold the soft output probabilities + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% FORWARD PASS PAM2,4,8 %%%%% - for k = 1:numTimeSteps - - % Compute the exponentials. (Use -llp_shifted if llp's are costs.) - exponents = -llp_(:, k); - - % Normalize to form a probability vector. - P_symbols(:, k) = exponents / sum(exponents); - end - - % Optionally, if you prefer a single soft output value per symbol (an expectation), - % define your PAM-4 constellation levels, e.g.: - soft_output = sum(P_symbols .* constellation, 1); % 1 x N vector of soft outputs + nml_LLP = LLP - max(LLP); + expLLP = exp(nml_LLP); %subtract highst value for better numerical stability, LLP's are not always close to zero + state_prob = expLLP ./ sum(expLLP); - %%%% BITWISE LLR's ??? %%%%% - figure(100); - clf - hold on - title('LLR between inner and outer bits') - bitmapping = PAMmapper(numel(constellation),0).demap(constellation); - for bp = 1:size(bitmapping,2) - pos_bitone = find(bitmapping(:,bp)==1); - pos_bitzero = find(bitmapping(:,bp)~=1); + if obj.M == 6 - llr_bits(bp,:) = min(llp(pos_bitone,:),[],1) - min(llp(pos_bitzero,:),[],1); + num_bits = 5; - subplot(size(bitmapping,2),1,bp) - scatter(1:length(data_ref),llr_bits(bp,:),1,'.'); - end - %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % all possible transitions (for now 36, including the "edges" + % of the QAM 32 constellation) + pam6transitions = combvec(obj.trellis_states,obj.trellis_states)'; % pam6transitions = + % [-5 -5; + % -3 -5; + % -1 -5; ... - % From: Log-Likelihood Probabilities LLP --> To: Log-Likelihood Ratios LLR - for s = 1:length(states)-1 - llr(s,:) = llp_(s,:) - llp_(s+1,:); % subtract llp's of successive const. points to get llr's - end - - % Build Sets of LLR's that contain only those values at - % timepoint k, where the symbols: - % a) were actually transmitted: set "S" - % b) were decoded by viterbi: set "SC" - S = NaN(length(states)-1,length(data_in)); - SC = NaN(length(states)-1,length(data_in)); + pam6bits = PAMmapper(6,0,"eth_style",0).demap(reshape(pam6transitions',[],1)./sqrt(10)); - llp_inf = llp_; - llp_inf(llp_==0) = Inf; - [~,scnd_idx] = min(llp_inf,[],1) ; - for s = 1:length(states)-1 + pam6bits = reshape(pam6bits',5,[])'; + %pam6bits = + % 0 0 0 1 0 + % 0 0 0 1 0 + % 0 0 0 1 1 + % 1 0 0 1 1 + % 1 0 0 1 0 + % 1 0 0 1 0 + % 0 0 1 1 0 + % .... - %%% SET "S" - % find all time indices k, where const point s was actually transmitted - indice = tx_symbolpos(s,:)==1; - S(s,indice) = llr(s,indice); - %calc mean of all llr's where symbol s was transmitted - K(s,1) = mean(S(s,indice),'omitnan'); - - % find all time indices k, where const. point s+1 was actually transmitted - indice_plusone = tx_symbolpos(s+1,:)==1; - S(s,indice_plusone) = llr(s,indice_plusone); - K(s,2) = mean(S(s,indice_plusone),'omitnan'); + [ok1, idx_bit_0] = ismember(pam6transitions(:,1), obj.trellis_states); + [ok2, idx2] = ismember(pam6transitions(:,2), obj.trellis_states); + assert(all(ok1)&all(ok2), 'Some transition amplitude not found in trellis_states') + pam6ind = [idx_bit_0, idx2]; - - %%% SET "SC" - % find all time indices k, where symbol s was decoded - % idx: find positions in time where a symbol s was decoded - idx = est_symbolpos_llm(s,:); - % idx 2: find positions where the strongest competitor is - % s+1, i.e. the second best llp is at s+1 - idx2 = scnd_idx == s+1; - SC(s,idx&idx2) = llr(s,idx&idx2); - KC(s,1) = mean(SC(s,idx&idx2),'omitnan'); + tx_bits_pam6_reshaped = reshape(tx_bits,5,[])'; - % find all time indices k, where symbol s+1 was decoded - % idx: find positions in time where a symbol s+1 was decoded - idx = est_symbolpos_llm(s+1,:); - idx2 = scnd_idx == s; - % idx 2: find positions where the strongest competitor is - % s, i.e. the second best llp is at s - SC(s,idx&idx2) = llr(s,idx&idx2); - KC(s,2) = mean(SC(s,idx&idx2),'omitnan'); + numPairs = floor(size(LLP,2)/2); + LLR_exact = zeros(numPairs,5); + LLR_maxlogmap = zeros(numPairs,5); - % scale the sets, using the average (?) of the - llrcn(s,:) = SC(s,:) * ( constellation(s+1)-constellation(s) ) ./ ( K(s,2)-K(s,1)) + decisionLevels(s); + for k = 1:numPairs + symbol1 = 2*k-1; + symbol2 = 2*k; - end - - figure(111) - title("Bla") - clf - for s = 1:length(states)-1 - figure(1111) - hold on - title(sprintf('llrcn = llp %d - llp %d',s, s+1,s, s+1)); - scatter(1:length(llrcn),llrcn(s,:),1,'.'); + LLP1 = LLP (:,symbol1); + LLP2 = LLP (:,symbol2); + prob1 = state_prob(:,symbol1); + prob2 = state_prob(:,symbol2); + % 36 joint‐metrics M = log P(i)*P(j) = L1(i)+L2(j) + Mij = LLP1(pam6ind(:,1)) + LLP2(pam6ind(:,2)); + pij = prob1(pam6ind(:,1)) .* prob2(pam6ind(:,2)); - % STUFENARTIGE LLR'S UND LLP'S %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - figure(111) - - subplot(1,length(states),s) - hold on - title(sprintf('llr = llp %d - llp %d \n SC=llr(tx sym = %d or %d)',s, s+1,s, s+1)); - scatter(1:length(llr),llr(s,:),1,'.'); - scatter(1:length(SC),SC(s,:),1,'.'); - yline([K(s,1),K(s,2)]); - low = min(min(llr)); - hi = max(max(llr)); - ylim([low,hi]); + % now for each of the 5 bits do exact-LLR or max-log + for b = 1:num_bits + idx_bit_0 = pam6bits(:,b)==1; + idx_bit_1 = pam6bits(:,b)==0; - subplot(1,length(states),length(states)) - hold on - scatter(1:length(llr),llr(s,:),1,'.'); - ylim([low,hi]); + %--- exact LLR from probabilities + P1 = sum(pij(idx_bit_0)); + P0 = sum(pij(idx_bit_1)); + LLR_exact(k,b) = log(P1./P0); + %--- max-log: + LLR_maxlogmap(k,b) = max( Mij(idx_bit_0) ) - max( Mij(idx_bit_1) ); % N x num_bits + end + end - % HISTOGRAMME %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - figure(112) - subplot(length(states),1,s) - hold on - title(sprintf('histogram of llr; between const point %d - %d',s, s+1)); - histogram(llrcn(s,:),10000,'EdgeAlpha',0) - % histogram(SC(s,:),10000,'EdgeAlpha',0) - xlim([-20, 20]); - subplot(length(states),1,length(states)) - hold on - histogram(llrcn(s,:),10000,'EdgeAlpha',0) - % histogram(SC(s,:),10000,'EdgeAlpha',0) - % xlim([-20, 20]); + MI = zeros(1, num_bits); + for k = 1:num_bits + + idx_bit_1 = (tx_bits_pam6_reshaped(:,k) == 0); %wo sind die 1en + idx_bit_0 = (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_bit_0,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; + + 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).demap(first_sym./rms(first_sym)); + + % 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: using max instead of sum) + 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); + LLR_exact = LLR_exact; + for k = 1:num_bits + + idx_bit_1 = (tx_bits(:,k) == 0); %wo sind die 1en + idx_bit_0 = (tx_bits(:,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_bit_0,k); + + % Calculate mutual information for bit position k + I0 = mean(log2(1 + exp(llr0(100:end-100)))); % exp(--LLR) = exp(positive) > 1 + I1 = mean(log2(1 + exp(-llr1(100:end-100)))); % exp(-+LLR) = exp(negative) < 1 + MI(k) = 1 - 0.5 * (I0 + I1); + end + + GMI = sum(MI); % Total mutual information for 2 symbols end - softdecisions = mean(llrcn,1,'omitnan'); + VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS); - distance = abs(VITERBI_ESTIMATION_SYMBOLS-llrcn); - [win_cost,win_idx] = min(distance,[],1); - for i = 1:length(data_in) - soft_decisions(i) = llrcn(win_idx(i),i); + debug = 0; + 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 end - soft_decisions = max(llrcn,[],1); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% CHECK BER's %%%%% - showLevelHistogram(soft_decisions,data_ref) + if debug + tx_bits = reshape(tx_bits',[],1); - figure() - % scatter(1:length(data_in),VITERBI_ESTIMATION_SYMBOLS,1,'.'); - scatter(1:length(data_in),soft_decisions,1,'.'); + % DECIDE based on Viterbi traceback + VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS); + rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(VITERBI_ESTIMATION_SYMBOLS'); + rx_bits = reshape(rx_bits',[],1); + [~,numErr,ber_viterbi,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + fprintf('Viterbi BER: %.2e \n',ber_viterbi); + fprintf('Viterbi Errors = %d\n', numErr); + % Convert LLR values to a hard-decision bit stream + bit_stream = LLR_maxlogmap > 0; %ratio separates lower or higher than =0 -> simply decode for the negative values + bit_stream = reshape(bit_stream',[],1); + [~,~,ber_llr,~] = calc_ber(bit_stream,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + fprintf('LLR MaxLogMAP BER : %.2e \n',ber_llr); - MLM_ESTIMATION(1:length(data_in)) = PAMmapper(numel(constellation),0).quantize(soft_decisions)'; - rx_bits = PAMmapper(numel(constellation),0).demap(MLM_ESTIMATION'); - [~,~,ber_mlm,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('MLM BER: %.2e \n',ber_mlm); + % Convert LLR values to a hard-decision bit stream + bit_stream = LLR_exact > 0; %ratio separates lower or higher than =0 -> simply decode for the negative value + bit_stream = reshape(bit_stream',[],1); + [~,~,ber_llr,~] = calc_ber(bit_stream,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + fprintf('LLR BER : %.2e \n',ber_llr); + + % DECIDE based on lowest LLP index and check BER + [~,llp_based_state_seq]=max(LLP); + LLP_EST(1:length(data_in)) = first_sym(llp_based_state_seq); + LLP_EST = LLP_EST./rms(LLP_EST); + rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(LLP_EST'); + rx_bits = reshape(rx_bits',[],1); + [~,~,ber_llp,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + fprintf('LLP BER : %.2e \n',ber_llp); + + % directly decide based on the FW path metrics + [~,fw_direct_state_seq]=max(alpha); + FW_EST(1:length(data_in)) = first_sym(fw_direct_state_seq); + FW_EST = FW_EST./rms(FW_EST); + rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(FW_EST'); + rx_bits = reshape(rx_bits',[],1); + [~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + fprintf('FW BER: %.2e \n',ber_fw); + + end end end diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index 926d016..73a4321 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -5,7 +5,7 @@ classdef DBHandler < handle properties conn % Database connection object - pathToDB % Path to the SQLite database + dataBase % Path to the SQLite database tableNames % Cell array containing names of all tables in the database tables = struct(); % Structure containing MATLAB tables for each database table distinctValues @@ -21,7 +21,7 @@ classdef DBHandler < handle % obj = DBHandler('pathToDB', 'path/to/database.db'); arguments - options.pathToDB = ""; % Default value for pathToDB if not provided + options.dataBase = ""; % Default value for pathToDB if not provided options.type = "mysql"; end @@ -37,12 +37,17 @@ classdef DBHandler < handle try if options.type == "sqlite" - obj.conn = sqlite(obj.pathToDB); + + obj.conn = sqlite(obj.dataBase); + elseif options.type == "mysql" - datasource = "jdbc:mysql://134.245.243.254:3306/labor"; + + % datasource = "jdbc:mysql://134.245.243.254:3306/labor"; + + obj.conn = database( ... - "labor", ... % Database name + string(obj.dataBase), ... % Database name "silas", ... % Username "silas", ... % Password (or getSecret) "Vendor", "MySQL", ... @@ -68,8 +73,10 @@ classdef DBHandler < handle function obj = refresh(obj) % Get table names and the first rows of each table to understand the structure + warning off obj.getTableNames(); obj.getTables(); + warning on % obj.getDistinctValues(); end @@ -201,20 +208,24 @@ classdef DBHandler < handle if isstruct(newRow) fields = fieldnames(newRow); - % Handle empty fields - emptyFields = structfun(@isempty, newRow); - if sum(emptyFields) > 0 - emptyFieldNames = fields(emptyFields); - for idx = 1:numel(emptyFieldNames) - newRow.(emptyFieldNames{idx}) = NaN; - end - end + % % Handle empty fields + % emptyFields = structfun(@isempty, newRow); + % if sum(emptyFields) > 0 + % emptyFieldNames = fields(emptyFields); + % for idx = 1:numel(emptyFieldNames) + % newRow.(emptyFieldNames{idx}) = NaN; + % end + % end - % Convert arrays to JSON strings - for i = 1:length(fields) - fieldValue = newRow.(fields{i}); - if isnumeric(fieldValue) && length(fieldValue) > 1 - newRow.(fields{i}) = jsonencode(fieldValue); + % Convert any non-scalar numeric (including [] and vectors) into JSON + for i = 1:numel(fields) + name = fields{i}; + value = newRow.(name); + + if isnumeric(value) && ~isscalar(value) + % jsonencode([]) -> "[]" + % jsonencode([a,b,c]) -> "[a,b,c]" + newRow.(name) = jsonencode(value); end end @@ -238,6 +249,7 @@ classdef DBHandler < handle elseif ischar(value) newRow.(colName{1}) = string(value); end + end % Parameters for retry logic @@ -251,7 +263,7 @@ classdef DBHandler < handle if obj.type == "mysql" newRow_ = obj.convertTableToCellStrings(newRow); end - sqlwrite(obj.conn, tableName, newRow); + sqlwrite(obj.conn, tableName, newRow,"Catalog",obj.dataBase); success = true; catch e if contains(e.message, 'database is locked') || contains(e.message, 'cannot rollback transaction') diff --git a/Classes/DataBaseHandler/Metricstruct.m b/Classes/DataBaseHandler/Metricstruct.m index 9161e47..403e90c 100644 --- a/Classes/DataBaseHandler/Metricstruct.m +++ b/Classes/DataBaseHandler/Metricstruct.m @@ -1,6 +1,6 @@ classdef Metricstruct % ResultData - Class to store and manage metric data from signal processing results - + properties result_id (1,1) double {mustBeNumeric} = NaN run_id (1,1) double {mustBeNumeric} = NaN @@ -24,15 +24,15 @@ classdef Metricstruct GMI (1,1) double {mustBeNumeric} = NaN AIR (1,1) double {mustBeNumeric} = NaN - Alpha (:,1) double {mustBeNumeric} = [] + Alpha (:,1) double {mustBeNumeric} = NaN MLSE_dir (:,1) double {mustBeNumeric} = [] end - + methods function obj = Metricstruct(varargin) % Constructor method for ResultData % Can be called empty or with name-value pairs - + % Process name-value pairs if provided if nargin > 0 for i = 1:2:nargin @@ -44,7 +44,7 @@ classdef Metricstruct end end end - + function s = toStruct(obj) % Convert the object to a struct s = struct(); @@ -53,7 +53,7 @@ classdef Metricstruct s.(props{i}) = obj.(props{i}); end end - + function str = toString(obj) % Convert the object to a formatted string s = obj.toStruct(); @@ -70,9 +70,62 @@ classdef Metricstruct end end end + + function print(obj) + % Print method to display key metrics in a formatted way + + % 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('%s\n', repmat('=', 1, nameWidth + valueWidth)); + + % Function to format numbers with appropriate precision + function str = formatNumber(value) + if isnan(value) + str = 'N/A'; + elseif abs(value) < 0.1 && value ~= 0 + str = sprintf('%.2e', value); + else + str = sprintf('%.4f', value); + end + end + + % Print each metric + % BER metrics + fprintf('%-*s: %*s\n', nameWidth, 'BER', valueWidth, formatNumber(obj.BER)); + fprintf('%-*s: %*s\n', nameWidth, 'BER (precoded)', valueWidth, formatNumber(obj.BER_precoded)); + + % SNR + fprintf('%-*s: %*s\n', nameWidth, 'SNR', valueWidth, formatNumber(obj.SNR)); + + % Information rates + fprintf('%-*s: %*s\n', nameWidth, 'GMI', valueWidth, formatNumber(obj.GMI)); + fprintf('%-*s: %*s GBd \n', nameWidth, 'AIR', valueWidth, formatNumber(obj.AIR.*1e-9)); + + % Alpha (if it's not empty, show first few values) + if ~isempty(obj.Alpha) + if length(obj.Alpha) <= 3 + alphaStr = sprintf('%.4f ', obj.Alpha); + else + alphaStr = sprintf('%.4f %.4f %.4f...', obj.Alpha(1:3)); + end + fprintf('%-*s: %s\n', nameWidth, 'Alpha', alphaStr); + else + fprintf('%-*s: %*s\n', nameWidth, 'Alpha', valueWidth, '[]'); + end + + % Print footer + fprintf('%s\n', repmat('=', 1, nameWidth + valueWidth)); + end + end - + methods (Static) + function obj = fromStruct(s) % Create a ResultData object from a struct obj = Metricstruct(); @@ -83,5 +136,6 @@ classdef Metricstruct end end end + end end \ No newline at end of file diff --git a/Classes/Warehouse_class/classes/DataStorage.m b/Classes/Warehouse_class/classes/DataStorage.m index 91530cf..f057c54 100644 --- a/Classes/Warehouse_class/classes/DataStorage.m +++ b/Classes/Warehouse_class/classes/DataStorage.m @@ -150,7 +150,7 @@ classdef DataStorage < handle end value{i} = tmp{1} ; else - value{i} = tmp{1} ; + value{i} = tmp ; end else diff --git a/Datatypes/adaption_method.m b/Datatypes/adaption_method.m new file mode 100644 index 0000000..5b4c12c --- /dev/null +++ b/Datatypes/adaption_method.m @@ -0,0 +1,9 @@ +classdef adaption_method < int32 + + enumeration + lms (1) + nlms (2) + rls (3) + 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 c3d6a63..dbb306b 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -5,9 +5,11 @@ arguments options.append_to_db = 0; options.max_occurences = 4; options.parameters = struct(); - options.database_path - options.database_name + options.database_type + options.dataBase + options.load_file_path = struct(); options.storage_path + options.mode end % Initialize output structures @@ -17,34 +19,72 @@ output.vnle_package = {}; output.dbtgt_package = {}; output.dbenc_package = {}; -% Initialize database connection -database = DBHandler("pathToDB", [options.database_path, options.database_name], "type", "sqlite"); -dataTable = queryRunid(run_id, database); -fsym = dataTable.symbolrate; -M = double(dataTable.pam_level); -duob_mode = db_mode(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 +if options.mode == "load_run_id" || options.append_to_db + % Initialize database connection + database = DBHandler("dataBase", [options.dataBase], "type", options.database_type ); end +if options.mode == "load_run_id" + + 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, options); + +elseif options.mode == "load_files" + + Tx_bits = load(options.load_file_path.tx_bits_path); + Symbols = load(options.load_file_path.tx_symbols_path); + Scpe_sig_raw = load(options.load_file_path.rx_raw_path); + + Tx_bits = Tx_bits.Bits; + Symbols = Symbols.Symbols; + Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; + + fsym = Symbols.fs; + M = Symbols.logbook.ModifierCopy{1}.M; + duob_mode = Symbols.logbook.ModifierCopy{1}.duobinary_mode; + + Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in", Scpe_sig_raw.fs, "fs_out", 2*fsym); + [~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + +else + + % Run quick Simulation + tx_simulation; + +end % Handle Settings and argument replacement len_tr = 4096*2; -ffe_order = [50, 5, 5]; +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.00; +mu_dc = 0.005; +dc_buffer_len = 1; + +mu_tr = 0; +mu_dd = 0.05; +adaption= 1; +use_dd_mode = 1; use_ffe = 1; +use_dfe = 1; use_vnle_mlse = 1; use_dbtgt = 1; -use_dbenc = 0; +use_dbenc = 1; addProcessingResultToDatabase = 0; @@ -60,74 +100,148 @@ if ~isempty(paramStruct) end % Configure equalizers -eq_lin = EQ("Ne",[50,0,0],"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",1); -pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); -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); -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); -% Duobinary signaling (db encoded) -mlse_db_enc = MLSE_viterbi("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, ... - "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); +options.max_occurences = min(options.max_occurences,length(Scpe_cell)); +for r = 1:options.max_occurences + + %FFE + % eq_dfe = 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); -% Load signal data -[Tx_bits, Symbols, Scpe_cell, ~] = loadSignalData(dataTable, options); + % + 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); + mlse_db_ = MLSE("DIR",[1,1],"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"); + % 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, ... + "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); -for r = 1:numel(Scpe_cell) - % Preprocess signal Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym); - + if duob_mode ~= db_mode.db_encoded + if use_ffe - ffe_results = ffe(eq_lin,M,Scpe_sig,Symbols,Tx_bits,... + + ffe_order = [50, 0, 0]; + eq_dfe = 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); + + dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... "precode_mode",duob_mode,... - 'showAnalysis',1,... + 'showAnalysis',0,... "postFFE",[],... "eth_style_symbol_mapping",0); - output.ffe_package{r} = ffe_results; + + output.ffe_package{r} = dfe_results; + + dfe_results.metrics.print; + if options.append_to_db - database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config); + database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config); end + end - + + if use_dfe + + ffe_order = [50, 5, 5]; + eq_dfe = EQ("Ne",ffe_order,"Nb",[2,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); + + dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... + "precode_mode",duob_mode,... + 'showAnalysis',0,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + + output.ffe_package{r} = dfe_results; + + dfe_results.metrics.print; + + if options.append_to_db + database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config); + end + + end + if use_vnle_mlse + + pf_ncoeffs = 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",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', 1, ... "postFFE", [],... "eth_style_symbol_mapping", 0); - output.mlse_package{r} = mlse_results; - output.vnle_package{r} = ffe_results; + + 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 + + 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', 1, ... + "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_dbtgt + dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... "precode_mode", duob_mode, ... 'showAnalysis', 0,... "postFFE", []); - output.dbtgt_package{r} = dbt_results; + + output.dbtgt_package{r} = dbt_results; + if options.append_to_db database.addProcessingResult(run_id, dbt_results.metrics, dbt_results.config); end + end end - + if duob_mode == db_mode.db_encoded + 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 database.addProcessingResult(run_id, db_results.metrics, db_results.config); end + end diff --git a/Functions/EQ_structures/duobinary_signaling.m b/Functions/EQ_structures/duobinary_signaling.m index 013e184..e47bc19 100644 --- a/Functions/EQ_structures/duobinary_signaling.m +++ b/Functions/EQ_structures/duobinary_signaling.m @@ -36,20 +36,21 @@ if ~isempty(options.postFFE) end % Process through MLSE -eq_signal = mlse_.process(eq_signal); +% [mlse_signal] = mlse_.process(eq_signal); +[mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); % Apply duobinary encoding and decoding -eq_signal = Duobinary().encode(eq_signal); -eq_signal = Duobinary().decode(eq_signal); +mlse_signal = Duobinary().encode(mlse_signal); +mlse_signal = Duobinary().decode(mlse_signal); % Demap symbols to bits -rx_bits = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).demap(eq_signal); +rx_bits = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).demap(mlse_signal); %% Calculate BER and metrics [bits_db, errors_db, ber_db, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); -% Calculate performance metrics -[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); +% Calculate performance metrics after duobinary FFE! +[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); %SNR of duobinary sequence - not directly comparable to [gmi] = calc_air(eq_signal, tx_symbols, "skip_front", 10000, "skip_end", 10000); air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M)); [evm_total, evm_lvl] = calc_evm(eq_signal, tx_symbols); diff --git a/Functions/EQ_structures/duobinary_target.m b/Functions/EQ_structures/duobinary_target.m index b5ff07c..22257d7 100644 --- a/Functions/EQ_structures/duobinary_target.m +++ b/Functions/EQ_structures/duobinary_target.m @@ -23,8 +23,8 @@ if ~isempty(options.postFFE) end mlse_.DIR = [1,1]; -mlse_sig_sd = mlse_.process(eq_signal); - +% mlse_sig_sd = mlse_.process(eq_signal); +[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd); % precoding to mitigate error propagation, most prominently used in @@ -73,6 +73,10 @@ end rx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); [bits_db,errors_db,ber_db,errorIndice_db] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); +alpha = arburg(eq_noise.signal,1);%pf_.coefficients(2); +alpha = alpha(2); +gmi_mlse = GMI_MLSE; +air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M)); db_results = struct(); db_results.metrics = Metricstruct; @@ -85,13 +89,10 @@ db_results.metrics.numBits = bits_db; db_results.metrics.numBitErr = errors_db; db_results.metrics.BER_precoded = ber_db_diff_precoded; db_results.metrics.numBitErr_precoded = errors_db_diff_precoded; -db_results.metrics.SNR = NaN; -db_results.metrics.SNR_level = NaN; -db_results.metrics.GMI = NaN; -db_results.metrics.AIR = NaN; -db_results.metrics.EVM = NaN; -db_results.metrics.EVM_level = NaN; +db_results.metrics.GMI = gmi_mlse; +db_results.metrics.AIR = air_mlse; db_results.metrics.MLSE_dir = mlse_.DIR; +db_results.metrics.Alpha = alpha; % Create DB results structure db_results.config = Equalizerstruct(); @@ -99,7 +100,7 @@ eq_.e = []; eq_.e2 = []; eq_.e3 = []; db_results.config.eq = jsonencode(eq_); -mlse_.DIR = []; +% mlse_.DIR = []; db_results.config.mlse = jsonencode(mlse_); db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse); db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)'; diff --git a/Functions/EQ_structures/ffe.m b/Functions/EQ_structures/ffe.m index f9ff57f..e3ecc04 100644 --- a/Functions/EQ_structures/ffe.m +++ b/Functions/EQ_structures/ffe.m @@ -36,6 +36,11 @@ if ~isempty(options.postFFE) toc end +try + ch_coefficients = arburg(eq_noise.signal,1); + channel_alpha = ch_coefficients(2); +end + % Hard decision on FFE output eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); @@ -66,10 +71,15 @@ end % Create FFE results structure ffe_results = struct(); +try + eq_.e = []; + eq_.e2 = []; + eq_.e3 = []; + eq_.b = []; + eq_.b2 = []; + eq_.b3 = []; +end -eq_.e = []; -eq_.e2 = []; -eq_.e3 = []; ffe_results.config = Equalizerstruct(); ffe_results.config.eq = jsonencode(eq_); ffe_results.config.equalizer_structure = int32(equalizer_structure.ffe); @@ -95,7 +105,7 @@ ffe_results.metrics.GMI = gmi; ffe_results.metrics.AIR = air; ffe_results.metrics.EVM = evm_total; ffe_results.metrics.EVM_level = evm_lvl; - +ffe_results.metrics.Alpha = channel_alpha; end @@ -140,24 +150,40 @@ end end function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, postFFE) -% Display analysis plots and metrics -figure(336); -showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after FFE'); + % Display analysis plots and metrics -if ~isempty(postFFE) - showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338); -end + % Initialize figure handles + % Corrected line - added tx_symbols as second positional argument + showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 100); -showEQfilter(eq_.e, eq_signal_sd.fs.*2); + warning off + showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 101); + figure(gcf);hold on; plot(((1:length(eq_noise.signal)) / eq_noise.fs) * 1e6,movmean(eq_noise.signal,2000,1), 'LineWidth',3,'Color','black') + warning on -figure(341); clf; -showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 341); + showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 102); -warning off -figure(400); clf; -showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400); + showEQNoisePSD(eq_noise, "fignum", 103, "displayname", 'Residual Noise after FFE'); + + % Figure 2: Post-FFE coefficients (if available) + if ~isempty(postFFE) + showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 104); + end + + try + figure(339); + showEQfilter(eq_.e_tr, eq_signal_sd.fs.*2,"displayname",'training','fignum',339); + showEQfilter(eq_.e, eq_signal_sd.fs.*2,"displayname",'dec. directed','fignum',339); + legend on + end + + try + figure(240); hold on; plot(pow2db(movmean(eq_.debug_struct.error_tr',100)));ylim([-30,3]);title('error training'); + + figure(241); hold on; plot(pow2db(movmean(eq_.debug_struct.update_tr',100)));title('update step training'); + + figure(242); hold on; plot(pow2db(movmean(eq_.debug_struct.update',1000)));title('update step dd'); + end + % eq_signal_sd.eye(eq_signal_sd.fs,M,"displayname",'Eye','fignum',105); -figure(401); clf; -showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401); -warning on end \ No newline at end of file diff --git a/Functions/EQ_structures/vnle_postfilter_mlse.m b/Functions/EQ_structures/vnle_postfilter_mlse.m index 90691d8..6610221 100644 --- a/Functions/EQ_structures/vnle_postfilter_mlse.m +++ b/Functions/EQ_structures/vnle_postfilter_mlse.m @@ -45,9 +45,10 @@ end eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); % Process through postfilter and MLSE -mlse_sig_sd = pf_.process(eq_signal_sd, eq_noise); +[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_sd,LLR,GMI_MLSE] = mlse_.process(mlse_sig_sd,tx_symbols); mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).quantize(mlse_sig_sd); %% Calculate BER based on precoding mode @@ -63,7 +64,17 @@ air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(dou [std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols); % MLSE metrics -alpha = pf_.coefficients(2); +alpha = arburg(eq_noise.signal,1);%pf_.coefficients(2); +alpha = alpha(2); +gmi_mlse = GMI_MLSE; +air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M)); + +%% Display analysis if requested +if options.showAnalysis + displayAnalysis(eq_noise,whitened_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, options.postFFE); +end + + %% Prepare output structures % Determine postFFE order @@ -94,19 +105,21 @@ ffe_results.metrics.GMI = gmi_vnle; ffe_results.metrics.AIR = air_vnle; ffe_results.metrics.EVM = evm_vnle_total; ffe_results.metrics.EVM_level = evm_vnle_lvl; -ffe_results.metrics.Alpha = []; +ffe_results.metrics.Alpha = alpha; +try + eq_.e = []; + eq_.e2 = []; + eq_.e3 = []; +end -% Create FFE results structure - -eq_.e = []; -eq_.e2 = []; -eq_.e3 = []; ffe_results.config = Equalizerstruct(); ffe_results.config.eq = jsonencode(eq_); ffe_results.config.equalizer_structure = int32(equalizer_structure.vnle); ffe_results.config.comment = 'function: vnle_postfilter_mlse - FFE part'; + + mlse_results = struct(); mlse_results.metrics = Metricstruct; mlse_results.metrics.result_id = NaN; @@ -118,12 +131,11 @@ mlse_results.metrics.numBits = numbits.mlse; mlse_results.metrics.numBitErr = errors.mlse; mlse_results.metrics.BER_precoded = bers.mlse_precoded; mlse_results.metrics.numBitErr_precoded = errors.mlse_precoded; -mlse_results.metrics.SNR = NaN; -mlse_results.metrics.SNR_level = NaN; -mlse_results.metrics.GMI = NaN; -mlse_results.metrics.AIR = NaN; -mlse_results.metrics.EVM = NaN; -mlse_results.metrics.EVM_level = NaN; +% mlse_results.metrics.SNR = NaN; +mlse_results.metrics.GMI = gmi_mlse; +mlse_results.metrics.AIR = air_mlse; +% mlse_results.metrics.EVM = NaN; +% mlse_results.metrics.EVM_level = NaN; mlse_results.metrics.Alpha = alpha; mlse_results.metrics.MLSE_dir = mlse_.DIR; @@ -131,15 +143,12 @@ mlse_results.metrics.MLSE_dir = mlse_.DIR; mlse_results.config = Equalizerstruct(); mlse_results.config.eq = jsonencode(eq_); -mlse_.DIR = []; +mlse_.DIR = length(mlse_.DIR)-1; mlse_results.config.mlse = jsonencode(mlse_); mlse_results.config.equalizer_structure = int32(equalizer_structure.vnle_pf_mlse); mlse_results.config.comment = 'function: vnle_postfilter_mlse - MLSE part'; -%% Display analysis if requested -if options.showAnalysis - displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, options.postFFE); -end + end @@ -206,7 +215,7 @@ end end -function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, postFFE) +function displayAnalysis(eq_noise, whitened_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, postFFE) % Display analysis plots and metrics figure(336); showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients); @@ -228,4 +237,9 @@ showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400); showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401); drawnow; warning on + + +whitened_noise.spectrum("displayname",'after postfilter','fignum',342); +eq_noise.spectrum("displayname",'before postfilter','fignum',342); + end \ No newline at end of file diff --git a/Functions/EQ_visuals/showEQNoisePSD.m b/Functions/EQ_visuals/showEQNoisePSD.m index 51b12d7..a10fb15 100644 --- a/Functions/EQ_visuals/showEQNoisePSD.m +++ b/Functions/EQ_visuals/showEQNoisePSD.m @@ -41,4 +41,7 @@ end % Ensure a legend is displayed legend('show'); end + + xlim([-eq_noise.fs/2* 1e-9 eq_noise.fs/2* 1e-9]); + end diff --git a/Functions/EQ_visuals/showEQcoefficients.m b/Functions/EQ_visuals/showEQcoefficients.m index a844155..cb747b8 100644 --- a/Functions/EQ_visuals/showEQcoefficients.m +++ b/Functions/EQ_visuals/showEQcoefficients.m @@ -53,8 +53,8 @@ function showEQcoefficients(options) for i = 1:numSubplots subplot(1, numSubplots, i); - stem(coeffs{i}, 'Color', options.color, 'LineWidth', 0.1, ... - 'Marker', '.', 'MarkerSize', 1); + stem(coeffs{i}, 'Color', options.color, 'LineWidth', 1, ... + 'Marker', '.', 'MarkerSize', 10); title(titles{i}); ylim([-1, 1]); % Set y-axis limits to [-1, 1] grid on; diff --git a/Functions/EQ_visuals/showEQfilter.m b/Functions/EQ_visuals/showEQfilter.m index 1d13022..3e0f4b0 100644 --- a/Functions/EQ_visuals/showEQfilter.m +++ b/Functions/EQ_visuals/showEQfilter.m @@ -1,5 +1,12 @@ -function showEQfilter(coefficients,fs) +function showEQfilter(coefficients,fs,options) + +arguments + coefficients + fs + options.fignum (1,1) double = NaN % Default to NaN if not provided + options.displayname (1,:) char = '' % Default to an empty string if not provided +end % Assuming that obj.e contains the final FFE filter coefficients. % Set the number of frequency points and sampling frequency. @@ -12,24 +19,28 @@ function showEQfilter(coefficients,fs) half_nfft = floor(nfft/2) + 1; f = f(1:half_nfft); H = H(1:half_nfft); - - % Plot the magnitude and phase responses. - figure(339); + + % Determine the figure number to use or create a new figure + if isnan(options.fignum) + fig = figure; % Create a new figure and get its handle + else + fig = figure(options.fignum); % Use the specified figure number + end % Magnitude response (in dB) subplot(2,1,1); hold on - plot(f.*1e-9, 20*log10(abs(1./H))); + plot(f.*1e-9, 20*log10(abs(1./H)),'DisplayName',options.displayname); title('(Inverted) Magnitude Response of FFE Filter'); - xlabel('Frequency (Hz)'); + xlabel('Frequency (GHz)'); ylabel('Magnitude (dB)'); grid on; % Phase response subplot(2,1,2); - plot(f.*1e-9, unwrap(angle(H))); + plot(f.*1e-9, unwrap(angle(H)),'DisplayName',options.displayname); title('Phase Response of FFE Filter'); - xlabel('Frequency (Hz)'); + xlabel('Frequency (GHz)'); ylabel('Phase'); grid on; diff --git a/Functions/EQ_visuals/showLevelHistogram.m b/Functions/EQ_visuals/showLevelHistogram.m index 437b65c..0f31bdc 100644 --- a/Functions/EQ_visuals/showLevelHistogram.m +++ b/Functions/EQ_visuals/showLevelHistogram.m @@ -20,10 +20,14 @@ end fig = figure(options.fignum); % Use the specified figure number end + eq_signal = max(min(eq_signal,3),-3); + %%% Separate Classes constellation = unique(ref_symbols); received_sd = NaN(numel(constellation),length(ref_symbols)); - lvlcol = cbrewer2('Set1',numel(constellation)); + lvlcol = cbrewer2('Paired',numel(constellation)*2); + lvlcol = lvlcol(2:2:end,:); + % lvlcol = cbrewer2('Set1',numel(constellation)); for lvl = 1:numel(constellation) %Separate the equalized signal into the %respective levels based on the actually @@ -43,6 +47,7 @@ end histogram(received_sd(lvl,:),1000,"EdgeAlpha",0,'DisplayName',['Lvl ',num2str(lvl),' ; ',num2str(cnt(lvl)),' '],'FaceColor',lvlcol(lvl,:),'Normalization','pdf'); warning on end + xlim([-3 3]); legend grid on diff --git a/Functions/EQ_visuals/showLevelScatter.m b/Functions/EQ_visuals/showLevelScatter.m index a5d34ef..70ddb39 100644 --- a/Functions/EQ_visuals/showLevelScatter.m +++ b/Functions/EQ_visuals/showLevelScatter.m @@ -7,7 +7,7 @@ arguments options.f_sym = []; end -plot_shit = 0; +plot_shit = 1; if isa(eq_signal,'Signal') options.f_sym = eq_signal.fs; @@ -25,6 +25,7 @@ if plot_shit fig = figure; % Create a new figure and get its handle else fig = figure(options.fignum); % Use the specified figure number + clf; end end @@ -127,5 +128,6 @@ if plot_shit yline(levels); xlabel('Time in $\mu$s'); ylabel('Normalized Amplitude'); +ylim([-3 3]); end end diff --git a/Functions/Job_Processing/loadSignalData.m b/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m similarity index 71% rename from Functions/Job_Processing/loadSignalData.m rename to Functions/Job_Processing/loadAndSyncSignalDataFromDb.m index cff4ff8..db2812c 100644 --- a/Functions/Job_Processing/loadSignalData.m +++ b/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m @@ -1,4 +1,4 @@ -function [Bits, Symbols, Scpe_cell, found_sync] = loadSignalData(dataTable, options) +function [Bits, Symbols, Scpe_cell, found_sync] = loadAndSyncSignalDataFromDb(dataTable, options) % LOADSIGNALDATA Loads and synchronizes signal data from storage % % Inputs: @@ -12,22 +12,30 @@ function [Bits, Symbols, Scpe_cell, found_sync] = loadSignalData(dataTable, opti % found_sync - Boolean indicating if synchronization was successful found_sync = 0; -tempLocalStorage = 1; +tempLocalStorage = 0; + +% Define the fixed storage directory relative to the user's MATLAB preferences directory +storage_dir = fullfile(prefdir, 'temp_sync_data'); % Part A: Check and load from local storage if available if tempLocalStorage == 1 - local_filename = sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id)); + local_filename = fullfile(storage_dir, sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id))); if exist(local_filename, 'file') % Load from local storage and return - load(local_filename, 'Bits', 'Symbols', 'Scpe_cell'); - found_sync = 1; + try + load(local_filename, 'Bits', 'Symbols', 'Scpe_cell'); + found_sync = 1; + return + catch + delete(local_filename); + end end end % If not locally saved, load from storage if ~found_sync % Load transmitted bits - Bits = load([options.storage_path, char(dataTable.tx_bits_path)]); + Bits = load(fullfile([options.storage_path, char(dataTable.tx_bits_path)])); Bits = Bits.Bits; % Map bits to symbols @@ -37,7 +45,7 @@ if ~found_sync Symbols_mapped.fs = fsym; % Load original symbols - Symbols = load([options.storage_path, char(dataTable.tx_symbols_path)]); + Symbols = load(fullfile([options.storage_path, char(dataTable.tx_symbols_path)])); Symbols = Symbols.Symbols; found_sync = 0; @@ -45,7 +53,7 @@ if ~found_sync % Try to load pre-synchronized data try - Scpe_load = load([options.storage_path, char(dataTable.rx_sync_path)]); + Scpe_load = load(fullfile([options.storage_path, char(dataTable.rx_sync_path)])); Scpe_cell = Scpe_load.S; [~,~,~,found_sync] = Scpe_cell{2}.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); catch @@ -74,21 +82,21 @@ end % Part B: Save to local storage if data was loaded and synced if tempLocalStorage == 1 && found_sync - local_filename = sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id)); - % Create directory if it doesn't exist - if ~exist('temp_sync_data', 'dir') - mkdir('temp_sync_data'); + if ~exist(storage_dir, 'dir') + mkdir(storage_dir); end +% local_filename = fullfile(storage_dir, sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id))); + % List existing files and remove oldest if more than N max_local_files = 10; % Store up to N files - files = dir('sync_data_run_*.mat'); + files = dir(fullfile(storage_dir, 'sync_data_run_*.mat')); if length(files) >= max_local_files % Sort by date [~, idx] = sort([files.datenum]); % Delete oldest file - delete(files(idx(1)).name); + delete(fullfile(storage_dir, files(idx(1)).name)); end % Save current data diff --git a/Functions/Job_Processing/submitJobs.m b/Functions/Job_Processing/submitJobs.m index a5a45a4..6cc834f 100644 --- a/Functions/Job_Processing/submitJobs.m +++ b/Functions/Job_Processing/submitJobs.m @@ -89,11 +89,13 @@ if submit_mode == processingMode.parallel % Submit job with proper arguments futures{jobCounter} = parfeval(@dsp_runid, 1, run_ids(r), ... - "database_name", dsp_options.database_name, ... + "database_type", dsp_options.database_type,... + "dataBase", dsp_options.dataBase, ... "append_to_db", dsp_options.append_to_db, ... - "database_path", dsp_options.database_path, ... + "load_file_path", dsp_options.load_file_path, ... "max_occurences", dsp_options.max_occurences, ... "storage_path", dsp_options.storage_path, ... + "mode", dsp_options.mode,... "parameters", optionalVars); fprintf('[RunID %d, Job %d] Submitted to pool.\n', run_ids(r), k); @@ -132,11 +134,13 @@ elseif submit_mode == processingMode.serial fprintf('[RunID %d, Job %d] Running in linear mode...\n', run_ids(r), k); results{k,r} = dsp_runid(run_ids(r),... - "database_name", dsp_options.database_name,... + "database_type", dsp_options.database_type,... + "dataBase", dsp_options.dataBase,... "append_to_db", dsp_options.append_to_db,... - "database_path", dsp_options.database_path,... + "load_file_path", dsp_options.load_file_path,... "max_occurences", dsp_options.max_occurences,... "storage_path", dsp_options.storage_path,... + "mode", dsp_options.mode,... "parameters", optionalVars); fprintf('[RunID %d, Job %d] Completed successfully.\n', run_ids(r), k); @@ -172,7 +176,7 @@ wh = submit_options.wh; function setupParallelPool() curpool = gcp('nocreate'); if isempty(curpool) - parpool; + parpool(10,"IdleTimeout",300); else if ~isempty(curpool.FevalQueue.QueuedFutures) || ~isempty(curpool.FevalQueue.RunningFutures) oldq = length(curpool.FevalQueue.QueuedFutures) + length(curpool.FevalQueue.RunningFutures); diff --git a/Functions/Metrics/calc_snr.m b/Functions/Metrics/calc_snr.m index 2dda8b0..d466022 100644 --- a/Functions/Metrics/calc_snr.m +++ b/Functions/Metrics/calc_snr.m @@ -23,8 +23,6 @@ function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise) levels = unique(tx_signal); % Preallocate an array to store the SNR for each unique level - snr_per_level = zeros(size(levels)); - % Loop over each unique level to compute the SNR for that level for i = 1:length(levels) % Find indices where tx_signal equals the current level @@ -32,5 +30,7 @@ function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise) % Compute the SNR for these indices snr_per_level(i) = snr(tx_signal(idx), eq_noise(idx)); + + histogram(eq_noise(idx)); end end diff --git a/Functions/Theory/analyze_moving_average_filter.m b/Functions/Theory/analyze_moving_average_filter.m new file mode 100644 index 0000000..43bcc5f --- /dev/null +++ b/Functions/Theory/analyze_moving_average_filter.m @@ -0,0 +1,64 @@ +% Parameters +N_values = [10 100 1000 4096]; % Different filter lengths to analyze +fs = 112e9; + +% Create figure +figure; + +% Plot frequency responses +subplot(211) +hold on +grid on +ylabel('Magnitude (dB)') +title('Frequency Response') +yline(-3,'--r') +ylim([-40 5]) + +subplot(212) +hold on +grid on +xlabel('Frequency (GHz)') +ylabel('Phase (rad)') +title('Phase Response') + +% Color map for different lines +colors = cbrewer2('Set1',length(N_values)); + +% Loop through different filter lengths +for i = 1:length(N_values) + N = N_values(i); + + % Filter coefficients + b = ones(1,N)/N; + a = 1; + + % Frequency response + [h,w] = freqz(b,a,4096*8); + freq = (w/(2*pi))*fs; + h_db = 20*log10(abs(h)); + + % Plot magnitude response + subplot(211) + plot(freq/1e9, h_db, 'Color', colors(i,:), 'DisplayName', sprintf('N=%d', N),'LineWidth',0.1) + + % Plot phase response + subplot(212) + plot(freq/1e9, unwrap(angle(h)), 'Color', colors(i,:), 'DisplayName', sprintf('N=%d', N),'LineWidth',0.1) + + % Find -3dB frequency + cutoff_idx = find(h_db <= -3, 1); + f_cutoff = freq(cutoff_idx)/1e9; + fprintf('N=%d: Cutoff frequency (-3dB point): %.2f GHz\n', N, f_cutoff) +end + +% Add legend and adjust axes +subplot(211) +legend('show') +xlim([0 16]) % Adjust x-axis limit to better see the differences + +subplot(212) +legend('show') +xlim([0 16]) % Adjust x-axis limit to better see the differences + +% Analytical approximation +f_3db_approx = 0.443 * fs./N_values ./ 1e9; \ No newline at end of file diff --git a/projects/ECOC_2025/auswertung_algorithms/mpi_dsp_debug.m b/projects/ECOC_2025/auswertung_algorithms/mpi_dsp_debug.m new file mode 100644 index 0000000..d765e30 --- /dev/null +++ b/projects/ECOC_2025/auswertung_algorithms/mpi_dsp_debug.m @@ -0,0 +1,60 @@ + +load("ffe_debug_snapshot.mat"); + + +dc_buffer_len = logspace(0,3,12); +dc_buffer_len = 1024; + +mu_dc = logspace(-3,0,24); + +parfor d = 1:length(mu_dc) + + eq_lin = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... + 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,... + "mu_dc",mu_dc(d),... + "dc_buffer_len",1024, ... + "ffe_buffer_len",1,... + "smoothing_buffer_length",0,... + "smoothing_buffer_update",1,... + "adaptive_mu_mode",0); + % + % eq_lin = FFE("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... + % 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0); + + ffe_results = ffe(eq_lin,M,Scpe_sig,Symbols,Tx_bits,... + "precode_mode",duob_mode,... + 'showAnalysis',0,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + + ffe_results.metrics.print + + % % eq_lin = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-4,"dfe_mu_dd",5e-4,"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",21,"dfe_order",2,"sps",2,"decide",0); + % + % eq_lin = FFE("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... + % 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0); + % pf_ = Postfilter("ncoeff",2,"useBurg",1); + % mlse_ = MLSE_viterbi("duobinary_output",0,'M',4,'trellis_states',PAMmapper(4,0).levels); + % + % [ffe_results2, mlse_results] = vnle_postfilter_mlse(eq_lin, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... + % "precode_mode", duob_mode,... + % 'showAnalysis', 1, ... + % "postFFE", [],... + % "eth_style_symbol_mapping", 0); + + ber(d) = ffe_results.metrics.BER; + + +end + +figure(10) +hold on +plot(mu_dc,ber,'LineWidth',1,'DisplayName',sprintf('DC buffer len = 1024'),'Marker','.','MarkerSize',10); +xlabel('BER'); +xlabel('MU DC'); +title('BER Optimization over dc\_buffer\_len'); +yline([4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); +ylim([9e-4, 0.5]); +set(gca, 'YScale', 'log'); % BER is usually plotted log-scale +legend('show', 'Location', 'best'); +grid on; \ No newline at end of file diff --git a/projects/ECOC_2025/auswertung_algorithms/run_offline_dsp.m b/projects/ECOC_2025/auswertung_algorithms/run_offline_dsp.m new file mode 100644 index 0000000..af3e9d7 --- /dev/null +++ b/projects/ECOC_2025/auswertung_algorithms/run_offline_dsp.m @@ -0,0 +1,118 @@ +% === SETTINGS === + +dsp_options.append_to_db = 0; +dsp_options.max_occurences = 15; +dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +dsp_options.database_name = 'silas_labor_newdsp_newstructure.db'; +dsp_options.storage_path = 'Z:\2024\sioe_labor\'; + +dsp_options.parameters = struct(); +dsp_options.parameters.mu_dc = [0.005]; + +% === Get Run ID's === +db = DBHandler("pathToDB", [dsp_options.database_path, dsp_options.database_name], "type", "sqlite"); +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 5108); +fp.where('Runs', 'is_mpi','EQUALS', 0); +fp.where('Runs', 'fiber_length','EQUALS', 1); +fp.where('Runs', 'wavelength','EQUALS', 1310); +fp.where('Runs', 'db_mode','EQUALS', 1); +fp.where('Runs', 'rop_attenuation','EQUALS', 0); +fp.where('Runs', 'pam_level','EQUALS', 4); +fp.where('Runs', 'bitrate','EQUALS', 360e9); +% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); +[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); + +% === Initialize DataStorage === +wh = DataStorage(dsp_options.parameters); +wh.addStorage("ffe_package"); +wh.addStorage("mlse_package"); +wh.addStorage("vnle_package"); +wh.addStorage("dbtgt_package"); +wh.addStorage("dbenc_package"); + +% === RUN IT === + +% [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true); +% wh.getStoValue('ffe_package',0.005); +% wh.getStoValue('mlse_package',0.005); + +[dataTable,~] = db.queryDB(fp, [db.getTableFieldNames('Runs');db.getTableFieldNames('Results');db.getTableFieldNames('Equalizer')]); + +dataTable = cleanUpTable(dataTable); + +% === Look at it === +y_var = 'BER_precoded'; +x_var = 'bitrate'; +fixedVars = {'equalizer_structure', x_var}; + +[dataTableClean, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var); + +% --- Group and aggregate --- +dataTableGrpd_mean = groupIt(fixedVars, dataTableClean, @mean); +dataTableGrpd_min = groupIt(fixedVars, dataTableClean, @min); +dataTableGrpd_max = groupIt(fixedVars, dataTableClean, @max); + +% Choose a color map +cols = linspecer(numel(unique(dataTableGrpd_mean.equalizer_structure))); + +figure; +hold on; + +% Get unique equalizer structures for grouping +unique_eq = unique(dataTableGrpd_mean.equalizer_structure); + +for i = 1:numel(unique_eq) + eq_val = unique_eq(i); + + % Filter grouped data for this equalizer structure + filt = dataTableGrpd_mean.equalizer_structure == eq_val; + + x = dataTableGrpd_mean.(x_var)(filt); + y_mean = dataTableGrpd_mean.(y_var)(filt); + y_min = dataTableGrpd_min.(y_var)(filt); + y_max = dataTableGrpd_max.(y_var)(filt); + + % Bounds for boundedline (distance from mean) + y_lower = y_mean - y_min; + y_upper = y_max - y_mean; + y_bounds = [y_lower, y_upper]; + + % --- Bounded line (mean ± min/max) --- + if exist('boundedline', 'file') + [hl, hp] = boundedline(x, y_mean, y_bounds, ... + 'alpha', 'transparency', 0.1, ... + 'cmap', cols(i,:), ... + 'nan', 'fill', ... + 'orientation', 'vert'); + set(hl, 'LineWidth', 1.2, 'DisplayName', sprintf('Eq %s', eq_val)); + set(hp, 'HandleVisibility', 'off'); + else + % If boundedline is not available, use errorbar + errorbar(x, y_mean, y_lower, y_upper, ... + 'o-', 'Color', cols(i,:), 'LineWidth', 1.2, ... + 'DisplayName', sprintf('Eq %d', eq_val),'HandleVisibility', 'off'); + end + + % --- Normal line (mean only) --- + plot(x, y_mean, '-', 'Color', cols(i,:), 'LineWidth', 1.5, ... + 'DisplayName', sprintf('Mean Eq %s', eq_val),'HandleVisibility', 'off'); + + % --- Scatter plot for individual points (from original data) --- + % Filter original data for this group + orig_filt = dataTableClean.equalizer_structure == eq_val; + x_scatter = dataTableClean.(x_var)(orig_filt); + y_scatter = dataTableClean.(y_var)(orig_filt); + + scatter(x_scatter, y_scatter, 10,cols(i,:), 'filled', ... + 'MarkerFaceAlpha', 0.5, 'DisplayName', sprintf('Scatter Eq %s', eq_val),'HandleVisibility', 'off'); +end + +yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); +set(gca, 'YScale', 'log'); % BER is usually plotted log-scale +xlabel(x_var, 'Interpreter', 'none'); +ylabel(y_var, 'Interpreter', 'none'); +legend('show', 'Location', 'best'); +grid on; +title(sprintf('%s vs. %s', y_var, x_var), 'Interpreter', 'none'); +hold off; \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m new file mode 100644 index 0000000..c73a50b --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m @@ -0,0 +1,224 @@ +% === SETTINGS === +dsp_options.append_to_db = 1; +dsp_options.max_occurences = 1; + +experiment = "highspeed_2024"; +dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' +dsp_options.load_file_path = struct(); + +if dsp_options.mode == "load_run_id" + + if experiment == "highspeed_2024" + + dsp_options.database_type = 'mysql'; + dsp_options.dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db'; + dsp_options.storage_path = 'Z:\2024\sioe_labor\'; + db = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type); + + elseif experiment == "mpi_ecoc_2025" + + dsp_options.database_type = 'mysql'; + dsp_options.dataBase = 'labor'; + dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\'; + db = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type); + + end + +elseif dsp_options.mode == "load_files" + + dsp_options.load_file_path.tx_bits_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091513_PAM_4_R_112_bits.mat"'; + dsp_options.load_file_path.tx_symbols_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091513_PAM_4_R_112_symbols.mat"'; + dsp_options.load_file_path.rx_raw_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091525_PAM_4_R_112_rec01_rx_signal_raw.mat"'; + +elseif dsp_options.mode == "simulate" + + error('Not yet implemented') + +end + +% === Get Run ID's === + +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 987); +% fp.where('Runs', 'pam_level','EQUALS', 4); +fp.where('Runs', 'bitrate','LESS_THAN', 310e9); +fp.where('Runs', 'fiber_length','EQUALS', 1); +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', 1310); +% fp.where('Runs', 'db_mode','EQUALS', 1); +fp.where('Runs', 'rop_attenuation','EQUALS', 0); +% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); + + +[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); + + +% Keep only the rows corresponding to the first occurrence of each 'sir' value +% [~, unique_indices] = unique(dataTable.sir, 'first'); +% dataTable = dataTable(unique_indices, :); + +% dataTable = dataTable(1,:); + +% === Set LOOPS & Initialize DataStorage === +dsp_options.parameters = struct(); +% dsp_options.parameters.pf_ncoeffs = [1,2];%[0,logspace(-4,0,10)]; + +wh = DataStorage(dsp_options.parameters); +wh.addStorage("ffe_package"); +wh.addStorage("mlse_package"); +wh.addStorage("vnle_package"); +wh.addStorage("dbtgt_package"); +wh.addStorage("dbenc_package"); + +% === RUN IT === + +[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true); + + +% wh.getStoValue('ffe_package',0.005); +% wh.getStoValue('mlse_package',0.005); + +% [dataTable,~] = db.queryDB(fp, [db.getTableFieldNames('Runs');db.getTableFieldNames('Results');db.getTableFieldNames('Equalizer')]); +% +% dataTable = cleanUpTable(dataTable); +% wh_analyze = wh_adap; +% % wh_analyze = wh_dcremoval_old2; +% % wh_analyze = wh_dcremoval_old2; +% +% res = cell(wh_analyze.parameter.mu_dc.length,wh_analyze.parameter.dc_buffer_len.length); +% ber_mean = zeros(wh_analyze.parameter.mu_dc.length,wh_analyze.parameter.dc_buffer_len.length); +% for m = 1:wh_analyze.parameter.mu_dc.length +% for b = 1:wh_analyze.parameter.dc_buffer_len.length +% res{m,b}=wh_analyze.getStoValue("ffe_package",wh_analyze.parameter.mu_dc.values(m),wh_analyze.parameter.dc_buffer_len.values(b)); +% try +% cells = res{m,b}{1}; +% idx = cellfun(@(c) ~isempty(c), cells); +% ber = cellfun(@(c) c.metrics.BER, cells(idx)); +% ber_mean(m,b) = mean(ber); +% catch +% ber_mean(m,b) = NaN; +% end +% end +% end +% +% figure() +% hold on +% ber_fix = cellfun(@(c) c.ffe_package{1}.metrics.BER, results_fix); +% ber_adap_m2 = cellfun(@(c) c.ffe_package{1}.metrics.BER, results_adap); +% ber_adap_method1 = cellfun(@(c) c.ffe_package{1}.metrics.BER, results); +% plot(dataTable.sir,ber_fix,'LineWidth',1,'DisplayName',sprintf('DCt; fix mu = 0.5; p=1024'),'Marker','.','MarkerSize',10); +% plot(dataTable.sir,ber_adap_method1,'LineWidth',1,'DisplayName',sprintf('DCt; adap mu 1; p=1024'),'Marker','.','MarkerSize',10); +% plot(dataTable.sir,ber_adap_m2,'LineWidth',1,'DisplayName',sprintf('DCt; adap mu 2; p=1024'),'Marker','.','MarkerSize',10); +% xlabel('BER'); +% xlabel('SIR'); +% yline([4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); +% % ylim([9e-4, 0.5]); +% set(gca, 'YScale', 'log'); % BER is usually plotted log-scale +% legend('show', 'Location', 'best'); +% grid on; +% +% +% +% +% figure; clf +% +% % Create meshgrid for contourf +% [X, Y] = meshgrid(dsp_options.parameters.mu_dc, dsp_options.parameters.dc_buffer_len); +% +% % Create contour plot +% contourf(X, Y, ber_mean', 20); % 20 contour levels, adjust as needed +% +% % Set axes to logarithmic scale +% set(gca, 'XScale', 'log', 'YScale', 'log'); +% +% colormap("parula"); +% c = colorbar; +% c.Label.String = 'BER'; +% +% xlabel('\mu_{dc}'); +% ylabel('dc\_buffer\_len'); +% title('BER Optimization over \mu_{dc} and dc\_buffer\_len'); +% +% % Make plot prettier +% grid on +% set(gca, 'Layer', 'top'); % Put grid lines on top of contours +% +% +% % === Look at it === +% y_var = 'BER_precoded'; +% x_var = 'bitrate'; +% fixedVars = {'equalizer_structure', x_var}; +% +% [dataTableClean, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var); +% +% % --- Group and aggregate --- +% dataTableGrpd_mean = groupIt(fixedVars, dataTableClean, @mean); +% dataTableGrpd_min = groupIt(fixedVars, dataTableClean, @min); +% dataTableGrpd_max = groupIt(fixedVars, dataTableClean, @max); +% +% % Choose a color map +% cols = linspecer(numel(unique(dataTableGrpd_mean.equalizer_structure))); +% +% figure; +% hold on; +% +% % Get unique equalizer structures for grouping +% unique_eq = unique(dataTableGrpd_mean.equalizer_structure); +% +% for i = 1:numel(unique_eq) +% eq_val = unique_eq(i); +% +% % Filter grouped data for this equalizer structure +% filt = dataTableGrpd_mean.equalizer_structure == eq_val; +% +% x = dataTableGrpd_mean.(x_var)(filt); +% y_mean = dataTableGrpd_mean.(y_var)(filt); +% y_min = dataTableGrpd_min.(y_var)(filt); +% y_max = dataTableGrpd_max.(y_var)(filt); +% +% % Bounds for boundedline (distance from mean) +% y_lower = y_mean - y_min; +% y_upper = y_max - y_mean; +% y_bounds = [y_lower, y_upper]; +% +% % --- Bounded line (mean ± min/max) --- +% if exist('boundedline', 'file') +% [hl, hp] = boundedline(x, y_mean, y_bounds, ... +% 'alpha', 'transparency', 0.1, ... +% 'cmap', cols(i,:), ... +% 'nan', 'fill', ... +% 'orientation', 'vert'); +% set(hl, 'LineWidth', 1.2, 'DisplayName', sprintf('Eq %s', eq_val)); +% set(hp, 'HandleVisibility', 'off'); +% else +% % If boundedline is not available, use errorbar +% errorbar(x, y_mean, y_lower, y_upper, ... +% 'o-', 'Color', cols(i,:), 'LineWidth', 1.2, ... +% 'DisplayName', sprintf('Eq %d', eq_val),'HandleVisibility', 'off'); +% end +% +% % --- Normal line (mean only) --- +% plot(x, y_mean, '-', 'Color', cols(i,:), 'LineWidth', 1.5, ... +% 'DisplayName', sprintf('Mean Eq %s', eq_val),'HandleVisibility', 'off'); +% +% % --- Scatter plot for individual points (from original data) --- +% % Filter original data for this group +% orig_filt = dataTableClean.equalizer_structure == eq_val; +% x_scatter = dataTableClean.(x_var)(orig_filt); +% y_scatter = dataTableClean.(y_var)(orig_filt); +% +% scatter(x_scatter, y_scatter, 10,cols(i,:), 'filled', ... +% 'MarkerFaceAlpha', 0.5, 'DisplayName', sprintf('Scatter Eq %s', eq_val),'HandleVisibility', 'off'); +% end +% +% yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); +% set(gca, 'YScale', 'log'); % BER is usually plotted log-scale +% xlabel(x_var, 'Interpreter', 'none'); +% ylabel(y_var, 'Interpreter', 'none'); +% legend('show', 'Location', 'best'); +% grid on; +% title(sprintf('%s vs. %s', y_var, x_var), 'Interpreter', 'none'); +% hold off; \ No newline at end of file diff --git a/projects/IMDD_base_system/imdd_model.m b/projects/IMDD_base_system/imdd_model.m index c15a919..70b7b29 100644 --- a/projects/IMDD_base_system/imdd_model.m +++ b/projects/IMDD_base_system/imdd_model.m @@ -134,6 +134,7 @@ if fsym_ ~= fsym fsym = fsym_; % fprintf('Adapted symbolrate to %d GBd, to match provided bitrate of %d GBit/s using PAM %d \n',fsym.*1e-9,bitrate.*1e-9, M); end + f_nyquist = fsym/2; %%% run the simulation or measurement or ... diff --git a/projects/IMDD_base_system/simulation_bwl.m b/projects/IMDD_base_system/simulation_bwl.m new file mode 100644 index 0000000..e1477a7 --- /dev/null +++ b/projects/IMDD_base_system/simulation_bwl.m @@ -0,0 +1,227 @@ +%%% Run parameters +% TX +M = 4; +fsym = 180e9; + +apply_pulsef = 1; +fdac = 256e9; +fadc = 256e9; +random_key = 1; + +db_precode = 0; +db_encode = 0; + +rcalpha = 0.05; +kover = 16; +vbias_rel = 0.5; +u_pi = 2.9; +vbias = -vbias_rel*u_pi; +laser_wavelength = 1310; +laser_linewidth = 0; +tx_bw_nyquist = 0.6; +rx_bw_nyquist = 0.6; + + +% Channel +link_length = 1; + +% RX +rop = -8; + + +vnle_order1 = 50; +vnle_order2 = 3; +vnle_order3 = 3; + +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; + + +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"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,"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(); + +Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); + + +%%%%% AWG +% El_sig = M8199A("kover",kover).process(Digi_sig); +El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); +% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); +% El_sig = El_sig.setPower(0,"dBm"); + +%%%%% Low-pass el. components %%%%%% +f_nyquist = fsym/2; +tx_bwl = tx_bw_nyquist.*f_nyquist; +% tx_bwl = 80e9; +El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); +% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); + +%%%%% Electrical Driver Amplifier %%%%%% +El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); +El_sig = El_sig.normalize("mode","oneone"); + +%%%%% MODULATE E/O CONVERSION %%%%%% +[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); + +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 = [-10]; +ber_vnle = []; +ber_mlse = []; +ber_viterbi = []; +ber_db = []; +ber_db_diff_precoded = []; +gmi_vnle = []; +gmi_mlse = []; +gmi_mlse_db = []; + +for r = 1:length(rop) + + + %%%%%% ROP %%%%%% + Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop(r)).process(Opt_sig); + + %%%%%% PD Square Law %%%%%% + Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig); + + %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% + rx_bwl = rx_bw_nyquist.*f_nyquist; + % rx_bwl = 80e9; + Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(Rx_sig); + + % %%%%%% Low-pass Scope %%%%%% + Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + + % Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); + + %%%%%% 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(Rx_sig); + + Scpe_sig_resampled = Scpe_sig.resample("fs_out",2*fsym); + + [~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + + + 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); + mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); + + + %Duobinary Targeting + if 0 + db_ref_sequence = Duobinary().encode(Symbols); + db_ref_constellation = unique(db_ref_sequence.signal); + [eq_signal, eq_noise] = eq_.process(Scpe_cell{1},db_ref_sequence); + + mlse_.DIR = [1,1]; + [mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); + + mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(mlse_sig_sd); + 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),~] = 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",0).demap(mlse_sig_hd); + [bits_mlse,errors_mlse,ber_db(r),~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + end + + % FFE or VNLE + 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); + [eq_signal_sd, eq_noise] = eq_.process(Scpe_cell{1}, Symbols); + [gmi_vnle(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100); + eq_signal_sd.plot("displayname",'bla','fignum',118); + + % Hard decision on VNLE output + eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); + rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); + [~,~,ber_vnle(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + fprintf('BER VNLE: %.2e \n',ber_vnle(r)); + fprintf('NGMI VNLE: %.2f \n',gmi_vnle(r)./log2(M)); + + + % Process through postfilter and MLSE + pf_ncoeffs = 1; + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + mlse_ = MLSE("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; + [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); + [~,~,ber_mlse(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + fprintf('BER: %.2e \n',ber_mlse(r)); + fprintf('NGMI MLSE: %.2f \n',gmi_mlse(r)./log2(M)); + + + % 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('FW BER: %.2e \n',ber_viterbi(r)); + +end + + +figure();hold on +plot(rop,gmi_mlse,'DisplayName','MLSE','Marker','*'); +plot(rop,gmi_vnle,'DisplayName','VNLE','Marker','*'); +plot(rop,gmi_mlse_db,'DisplayName','MLSE DB Tgt','Marker','*'); +ylim([0 2.6]); + +figure();hold on +plot(rop,ber_vnle,'DisplayName','VNLE'); + +plot(rop,ber_mlse,'DisplayName','MLSE','Marker','*'); +plot(rop,ber_viterbi,'DisplayName','Viterbi','Marker','*'); +plot(rop,ber_db_diff_precoded,'DisplayName','MLSE db diff','Marker','*'); +plot(rop,ber_db,'DisplayName','MLSE db','Marker','*'); +set(gca, 'yscale', 'log'); +legend diff --git a/projects/IMDD_base_system/simulation_bwl_2.m b/projects/IMDD_base_system/simulation_bwl_2.m new file mode 100644 index 0000000..8dff4a2 --- /dev/null +++ b/projects/IMDD_base_system/simulation_bwl_2.m @@ -0,0 +1,144 @@ +%%% Run parameters +% TX +M = 4; +fsym = 180e9; + +apply_pulsef = 1; +fdac = 256e9; +fadc = 256e9; +random_key = 1; + +db_precode = 0; +db_encode = 0; + +rcalpha = 0.05; +kover = 16; +vbias_rel = 0.5; +u_pi = 2.9; +vbias = -vbias_rel*u_pi; +laser_wavelength = 1310; +laser_linewidth = 0; +tx_bw_nyquist = 0.7; + +% Channel +link_length = 1; + +% RX +rop = -8; +rx_bw_nyquist = 0.7; + +vnle_order1 = 50; +vnle_order2 = 3; +vnle_order3 = 3; + +vnle_order=[vnle_order1,vnle_order2,vnle_order3]; +dfe_order = [0 0 0]; + +pf_ncoeffs = 1; + +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; + + +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"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,"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(); + +Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); + + + +%%%%% AWG +% El_sig = M8199A("kover",kover).process(Digi_sig); +El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); +% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); +% El_sig = El_sig.setPower(0,"dBm"); + +%%%%% Low-pass el. components %%%%%% +f_nyquist = fsym/2; +tx_bwl = tx_bw_nyquist.*f_nyquist; +% tx_bwl = 80e9; +El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); +% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); + +%%%%% Electrical Driver Amplifier %%%%%% +El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); +El_sig = El_sig.normalize("mode","oneone"); + +%%%%% MODULATE E/O CONVERSION %%%%%% +[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); + +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 %%%%%% +Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); + +%%%%%% PD Square Law %%%%%% +Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig); + +%%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% +rx_bwl = rx_bw_nyquist.*f_nyquist; +% rx_bwl = 80e9; +Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(Rx_sig); + +% %%%%%% Low-pass Scope %%%%%% +Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + +% Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); + +%%%%%% 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(Rx_sig); + +Scpe_sig_resampled = Scpe_sig.resample("fs_out",2*fsym); + +[~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + +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); +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); + +% 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; + +constellation = [-3, -1, 1, 3]; +chatgpt_answer(mlse_sig_sd.signal, Symbols.signal,mlse_.DIR,constellation); + +% 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); diff --git a/projects/IMDD_base_system/tx_simulation.m b/projects/IMDD_base_system/tx_simulation.m index 77a1679..1cd6216 100644 --- a/projects/IMDD_base_system/tx_simulation.m +++ b/projects/IMDD_base_system/tx_simulation.m @@ -1,6 +1,6 @@ - Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rcalpha); + Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rcalpha); [Digi_sig,Symbols,Tx_bits] = PAMsource(... "fsym",fsym,"M",M,"order",19,"useprbs",1,... diff --git a/projects/Job_Processing/run_offline_dsp.m b/projects/Job_Processing/run_offline_dsp.m index 2544680..335b57b 100644 --- a/projects/Job_Processing/run_offline_dsp.m +++ b/projects/Job_Processing/run_offline_dsp.m @@ -32,7 +32,7 @@ wh.addStorage("dbenc_package"); % === RUN IT === -% [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); +[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); % wh.getStoValue('ffe_package',0.005); % wh.getStoValue('mlse_package',0.005); diff --git a/projects/Job_Processing/run_offline_dsp_script.m b/projects/Job_Processing/run_offline_dsp_script.m new file mode 100644 index 0000000..a75b3bc --- /dev/null +++ b/projects/Job_Processing/run_offline_dsp_script.m @@ -0,0 +1,116 @@ +dsp_options.append_to_db = false; +dsp_options.max_occurences = 1; +dsp_options.mode = "load_run_id"; +experiment = "highspeed_2024"; + +if dsp_options.mode == "load_run_id" + + dsp_options.load_file_path = []; + if experiment == 'highspeed_2024' + + dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db'; + dsp_options.storage_path = 'Z:\2024\sioe_labor\'; + db = DBHandler("pathToDB", [dsp_options.database_path], "type", "sqlite"); + + elseif 'mpi_ecoc_2025' + + dsp_options.database_path = []; %sqlite im netzwerk + dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\'; + db = DBHandler("pathToDB", [dsp_options.database_path], "type", "mysql"); + + end + + +elseif dsp_options.mode == "load_files" + + dsp_options.load_file_path.tx_bits_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091513_PAM_4_R_112_bits.mat"'; + dsp_options.load_file_path.tx_symbols_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091513_PAM_4_R_112_symbols.mat"'; + dsp_options.load_file_path.rx_raw_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091525_PAM_4_R_112_rec01_rx_signal_raw.mat"'; + +elseif dsp_options.mode == "simulate" + + error('Not yet implemented') + +end + + +%% === Define Database Filters === + +if experiment == 'highspeed_2024' + + fp = QueryFilter(); + db = db.getDistinctValues; + %fp.where('Runs', 'run_id','EQUALS', 3); + fp.where('Runs', 'pam_level','EQUALS', 4); + allrates = string(num2str(sort(db.distinctValues.Runs.bitrate.*1e-9))); + fp.where('Runs', 'bitrate','EQUALS',390e9); + alllengths = string(num2str(sort(db.distinctValues.Runs.fiber_length))); + fp.where('Runs', 'fiber_length','EQUALS', 1); + 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', 1310); + fp.where('Runs', 'db_mode','EQUALS', 0); + % fp.where('Runs', 'rop_attenuation','EQUALS', 0); + fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); + +elseif experiment == 'mpi_ecoc_2025' + + fp = QueryFilter(); + %fp.where('Runs', 'run_id','EQUALS', 3); + fp.where('Runs', 'pam_level','EQUALS', 4); + fp.where('Runs', 'bitrate','EQUALS',224e9); + fp.where('Runs', 'is_mpi','EQUALS', 1); + 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', 0); + % fp.where('Runs', 'rop_attenuation','EQUALS', 0); + fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); + % fp.where('Runs', 'loop_id','EQUALS', 14); + +end + + +% === Queue from DB === +[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); + +% === Adapt or filter Matlab Table === +% [~, unique_indices] = unique(dataTable.sir, 'first'); +% dataTable = dataTable(unique_indices, :); + + +% === Set LOOPS & Initialize DataStorage === +dsp_options.parameters = struct(); +% dsp_options.parameters.adaption = [3]; +% dsp_options.parameters.mu_tr = 0.999;%[0,logspace(-4,0,10)]; +% dsp_options.parameters.mu_dd = [logspace(-0.004,0,20)]; +% dsp_options.parameters.use_dd_mode = [1]; + +wh = DataStorage(dsp_options.parameters); +wh.addStorage("ffe_package"); +wh.addStorage("mlse_package"); +wh.addStorage("vnle_package"); +wh.addStorage("dbtgt_package"); +wh.addStorage("dbenc_package"); + + +% === RUN IT === +% run this function: dsp_runid(run_id, options) +[results,wh] = submitJobs(dataTable.run_id, dsp_options, "serial", 'wh', wh, 'waitbar', false); + + +%% +BERs = cellfun(@(c) c.ffe_package{1,1}.metrics.BER, results); +figure +hold on +plot(dsp_options.parameters.mu_dd,BERs,'Marker','+') +yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); +set(gca, 'YScale', 'log'); % BER is usually plotted log-scale +xlabel('$\mu$ DD', 'Interpreter', 'latex'); +ylabel('BER', 'Interpreter', 'latex'); +legend('show', 'Location', 'best'); +grid on; +hold off; \ No newline at end of file diff --git a/test/duobinary_minimal_example.m b/test/duobinary_minimal_example.m index 8866e69..c39183e 100644 --- a/test/duobinary_minimal_example.m +++ b/test/duobinary_minimal_example.m @@ -46,66 +46,55 @@ end Tx_bits = Informationsignal(bitpattern); %%%%% Duobinary %%%%%% - -precode = 1; -db_encode = 0; - close all -Symbols_tx = PAMmapper(M,0).map(Tx_bits); +Symbols_tx = PAMmapper(M,0,"eth_style",0).map(Tx_bits); Symbols_tx.fs = fsym; +precode = db_mode.db_precoded; + %%% precode -if precode - Symbols0 = Duobinary().precode(Symbols_tx); -else - Symbols0 = Symbols_tx; +switch precode + case db_mode.db_precoded + Symbols_tx = Duobinary().precode(Symbols_tx); + case db_mode.db_encoded + Symbols_tx = Duobinary().precode(Symbols_tx); + Symbols_tx = Duobinary().encode(Symbols_tx); + case db_mode.no_db + end -figure;histogram(Symbols0.signal); +for n = 10 -for n = 0:200 + Symbols_rx = Symbols_tx; - if db_encode - Symbols1 = Duobinary().encode(Symbols0); - else - Symbols1 = Symbols0; - end - - Symbols2 = Symbols1; pos = 1; if n~=0 for pos = 1:n po = randi(100); - a = Symbols2.signal(100+pos) == Symbols1.signal(100+po); + a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po); while a == 1 po = po+1; po = randi(100); - a = Symbols2.signal(100+pos) == Symbols1.signal(100+po); + a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po); end - Symbols2.signal(100+pos) = Symbols1.signal(100+po); + Symbols_rx.signal(100+pos) = Symbols_tx.signal(100+po); end end - % disp(Symbols2.signal(100:100+pos)==Symbols1.signal(100:100+pos)) + error_positions = ~(Symbols_rx.signal == Symbols_tx.signal); + error_positions = find(error_positions==1); - %%% encode + switch precode + + case db_mode.db_precoded + Symbols_rx = Duobinary().encode(Symbols_rx); + Symbols_rx = Duobinary().decode(Symbols_rx); + case db_mode.db_encoded + Symbols_rx = Duobinary().decode(Symbols_rx); - if db_encode - - % figure;histogram(Symbols2.signal); - - Symbols3 = Duobinary().decode(Symbols2); - % figure;histogram(Symbols3.signal); - % autoArrangeFigures; - elseif precode - Symbols3 = Duobinary().encode(Symbols2); - Symbols3 = Duobinary().decode(Symbols3); - % figure;histogram(Symbols3.signal); - else - Symbols3 = Symbols2; end - - Rx_bits = PAMmapper(M,0).demap(Symbols3); + + Rx_bits = PAMmapper(M,0).demap(Symbols_rx); %%%%% Check BER of Bit Sequence %%%%%% @@ -113,14 +102,10 @@ for n = 0:200 % disp(['BER: ',sprintf('%.1E',ber),sprintf(' - Num. Err: %.1d',error_num(n+1)-2),' - - PAM-',num2str(M)]); fprintf('n: %d - Num. Err: %.1d \n',n,error_num(n+1)); + end -figure() -hold on -scatter(1:length(Symbols3),Symbols3.signal,1,'.'); -scatter(error_pos,Symbols3.signal(error_pos),14,'o'); - figure(3); clf @@ -128,8 +113,8 @@ clf subplot(2,2,1) hold on title('First Bits') -stairs(Tx_bits.signal(1:100,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits'); -stairs(Rx_bits.signal(1:100,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':') +stairs(Tx_bits.signal(100:150,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits'); +stairs(Rx_bits.signal(100:150,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':') legend subplot(2,2,2) @@ -143,12 +128,12 @@ subplot(2,2,3) hold on title('First Symbols Compare') stairs(Symbols_tx.signal(1:100,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-') -stairs(Symbols.signal(1:100,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols'); +stairs(Symbols_rx.signal(1:100,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols'); legend subplot(2,2,4) hold on title('Last Symbols Compare') stairs(Symbols_tx.signal(end-50:end,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-') -stairs(Symbols.signal(end-50:end,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols'); +stairs(Symbols_rx.signal(end-50:end,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols'); legend \ No newline at end of file From f16475c6ed2b0a7b30fad9c85f5c4fdfd835ad52 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 9 Jul 2025 10:59:00 +0200 Subject: [PATCH 04/30] eq structure --- Datatypes/equalizer_structure.m | 9 +++++---- Functions/EQ_structures/dsp_runid.m | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Datatypes/equalizer_structure.m b/Datatypes/equalizer_structure.m index 7e338bd..c8250a6 100644 --- a/Datatypes/equalizer_structure.m +++ b/Datatypes/equalizer_structure.m @@ -2,11 +2,12 @@ classdef equalizer_structure < int32 enumeration ffe (0) - vnle (1) - vnle_pf_mlse (2) + dfe (1) + vnle (2) + vnle_pf_mlse (3) % db_precoded (3) - vnle_db_mlse (3) - db_encoded (4) + vnle_db_mlse (4) + db_encoded (5) 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 dbb306b..1dc618d 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -141,6 +141,7 @@ for r = 1:options.max_occurences output.ffe_package{r} = dfe_results; dfe_results.metrics.print; + dfe_results.config.equalizer_structure = "ffe"; if options.append_to_db database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config); @@ -160,6 +161,7 @@ for r = 1:options.max_occurences "eth_style_symbol_mapping",0); output.ffe_package{r} = dfe_results; + dfe_results.config.equalizer_structure = "dfe"; dfe_results.metrics.print; @@ -184,6 +186,7 @@ for r = 1:options.max_occurences "eth_style_symbol_mapping", 0); ffe_results.metrics.print; + ffe_results.config.equalizer_structure = "vnle"; mlse_results.metrics.print; output.mlse_package{r} = mlse_results; From eed71b561087fc7a9c31441542d3b18aa03e79f9 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Thu, 10 Jul 2025 10:09:26 +0200 Subject: [PATCH 05/30] small changes in submission script --- Functions/EQ_structures/dsp_runid.m | 471 +++++++++--------- Functions/Job_Processing/submitJobs.m | 311 ++++++------ .../Auswertung_JLT/run_dsp_from_db.m | 12 +- 3 files changed, 415 insertions(+), 379 deletions(-) diff --git a/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m index 1dc618d..50dfc46 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -5,249 +5,268 @@ arguments options.append_to_db = 0; options.max_occurences = 4; options.parameters = struct(); - options.database_type + options.database_type options.dataBase options.load_file_path = struct(); options.storage_path options.mode end -% Initialize output structures -output.ffe_package = {}; -output.mlse_package = {}; -output.vnle_package = {}; -output.dbtgt_package = {}; -output.dbenc_package = {}; - -if options.mode == "load_run_id" || options.append_to_db - % Initialize database connection - database = DBHandler("dataBase", [options.dataBase], "type", options.database_type ); -end - -if options.mode == "load_run_id" - - 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, options); - -elseif options.mode == "load_files" - - Tx_bits = load(options.load_file_path.tx_bits_path); - Symbols = load(options.load_file_path.tx_symbols_path); - Scpe_sig_raw = load(options.load_file_path.rx_raw_path); - - Tx_bits = Tx_bits.Bits; - Symbols = Symbols.Symbols; - Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; - - fsym = Symbols.fs; - M = Symbols.logbook.ModifierCopy{1}.M; - duob_mode = Symbols.logbook.ModifierCopy{1}.duobinary_mode; - - Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in", Scpe_sig_raw.fs, "fs_out", 2*fsym); - [~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); - -else - - % Run quick Simulation - tx_simulation; - -end - -% Handle Settings and argument replacement - -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; - -use_ffe = 1; -use_dfe = 1; -use_vnle_mlse = 1; -use_dbtgt = 1; -use_dbenc = 1; - -addProcessingResultToDatabase = 0; - -% Overwrite default parameters if given in options.parameters -paramStruct = options.parameters; -if ~isempty(paramStruct) - paramNames = fieldnames(paramStruct); - for i = 1:numel(paramNames) - thisName = paramNames{i}; - thisValue = paramStruct.(thisName); - eval([thisName ' = thisValue;']); - end -end - -% Configure equalizers - -options.max_occurences = min(options.max_occurences,length(Scpe_cell)); -for r = 1:options.max_occurences - - %FFE - % eq_dfe = 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); +try - % - 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); - mlse_db_ = MLSE("DIR",[1,1],"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"); + % Initialize output structures + output.ffe_package = {}; + output.mlse_package = {}; + output.vnle_package = {}; + output.dbtgt_package = {}; + output.dbenc_package = {}; - % 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, ... - "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); + if options.mode == "load_run_id" || options.append_to_db + % Initialize database connection + database = DBHandler("dataBase", [options.dataBase], "type", options.database_type ); - % Preprocess signal - Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym); + % 2. Check if an equalizer configuration with the same hash exists + queryStr = sprintf('SELECT COUNT(DISTINCT eq_id) AS unique_eq_count, COUNT(*) AS entries_for_run FROM `Results` WHERE run_id = %d', run_id); + existing_results = database.fetch(queryStr); - if duob_mode ~= db_mode.db_encoded - - if use_ffe - - ffe_order = [50, 0, 0]; - eq_dfe = 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); - - dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... - "precode_mode",duob_mode,... - 'showAnalysis',0,... - "postFFE",[],... - "eth_style_symbol_mapping",0); - - output.ffe_package{r} = dfe_results; - - dfe_results.metrics.print; - dfe_results.config.equalizer_structure = "ffe"; - - if options.append_to_db - database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config); + if existing_results.unique_eq_count >= 6 + if (existing_results.entries_for_run / existing_results.unique_eq_count) > 8 + return end - - end - - if use_dfe - - ffe_order = [50, 5, 5]; - eq_dfe = EQ("Ne",ffe_order,"Nb",[2,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); - - dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... - "precode_mode",duob_mode,... - 'showAnalysis',0,... - "postFFE",[],... - "eth_style_symbol_mapping",0); - - output.ffe_package{r} = dfe_results; - dfe_results.config.equalizer_structure = "dfe"; - - dfe_results.metrics.print; - - if options.append_to_db - database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config); - end - - end - - if use_vnle_mlse - - pf_ncoeffs = 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",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', 1, ... - "postFFE", [],... - "eth_style_symbol_mapping", 0); - - ffe_results.metrics.print; - ffe_results.config.equalizer_structure = "vnle"; - 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 - - 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', 1, ... - "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_dbtgt - - dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... - "precode_mode", duob_mode, ... - 'showAnalysis', 0,... - "postFFE", []); - - output.dbtgt_package{r} = dbt_results; - - if options.append_to_db - database.addProcessingResult(run_id, dbt_results.metrics, dbt_results.config); - end - - end - end - - if duob_mode == db_mode.db_encoded - - 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 - database.addProcessingResult(run_id, db_results.metrics, db_results.config); end end + if options.mode == "load_run_id" + 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, options); + + elseif options.mode == "load_files" + + Tx_bits = load(options.load_file_path.tx_bits_path); + Symbols = load(options.load_file_path.tx_symbols_path); + Scpe_sig_raw = load(options.load_file_path.rx_raw_path); + + Tx_bits = Tx_bits.Bits; + Symbols = Symbols.Symbols; + Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; + + fsym = Symbols.fs; + M = Symbols.logbook.ModifierCopy{1}.M; + duob_mode = Symbols.logbook.ModifierCopy{1}.duobinary_mode; + + Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in", Scpe_sig_raw.fs, "fs_out", 2*fsym); + [~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + + else + + % Run quick Simulation + tx_simulation; + + end + + % Handle Settings and argument replacement + + 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; + + use_ffe = 1; + use_dfe = 1; + use_vnle_mlse = 1; + use_dbtgt = 1; + use_dbenc = 1; + + addProcessingResultToDatabase = 0; + + % Overwrite default parameters if given in options.parameters + paramStruct = options.parameters; + if ~isempty(paramStruct) + paramNames = fieldnames(paramStruct); + for i = 1:numel(paramNames) + thisName = paramNames{i}; + thisValue = paramStruct.(thisName); + eval([thisName ' = thisValue;']); + end + end + + % Configure equalizers + + options.max_occurences = min(options.max_occurences,length(Scpe_cell)); + for r = 1:options.max_occurences + + %FFE + % eq_dfe = 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_ = 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); + mlse_db_ = MLSE("DIR",[1,1],"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"); + + % 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, ... + "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); + + % Preprocess signal + Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym); + + if duob_mode ~= db_mode.db_encoded + + if use_ffe + + ffe_order = [50, 0, 0]; + eq_dfe = 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); + + dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... + "precode_mode",duob_mode,... + 'showAnalysis',0,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + + output.ffe_package{r} = dfe_results; + + dfe_results.metrics.print; + dfe_results.config.equalizer_structure = "ffe"; + + if options.append_to_db + database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config); + end + + end + + if use_dfe + + ffe_order = [50, 5, 5]; + eq_dfe = EQ("Ne",ffe_order,"Nb",[2,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); + + dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... + "precode_mode",duob_mode,... + 'showAnalysis',0,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + + output.ffe_package{r} = dfe_results; + dfe_results.config.equalizer_structure = "dfe"; + + dfe_results.metrics.print; + + if options.append_to_db + database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config); + end + + end + + if use_vnle_mlse + + pf_ncoeffs = 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",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', 1, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + ffe_results.metrics.print; + ffe_results.config.equalizer_structure = "vnle"; + 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 + + 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', 1, ... + "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_dbtgt + + dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, ... + 'showAnalysis', 0,... + "postFFE", []); + + output.dbtgt_package{r} = dbt_results; + + if options.append_to_db + database.addProcessingResult(run_id, dbt_results.metrics, dbt_results.config); + end + + end + end + + if duob_mode == db_mode.db_encoded + + 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 + database.addProcessingResult(run_id, db_results.metrics, db_results.config); + end + + end + + + end + +catch ME + save('workerError.mat','ME'); + rethrow(ME); end diff --git a/Functions/Job_Processing/submitJobs.m b/Functions/Job_Processing/submitJobs.m index 6cc834f..d667cc8 100644 --- a/Functions/Job_Processing/submitJobs.m +++ b/Functions/Job_Processing/submitJobs.m @@ -1,217 +1,234 @@ -function [results,wh] = submitJobs(run_ids, dsp_options, submit_mode, submit_options) -% SUBMITJOBS Submits dsp_runid jobs for processing +function [results, wh] = submitJobs(run_ids, dsp_options, submit_mode, submit_options) +% SUBMITJOBS Submits dsp_runid jobs for processing (parallel or serial) % -% Inputs: -% run_ids - Single run ID or array of Run IDs for processing -% dsp_options - Options for dsp_runid function -% submit_mode - Execution mode: 'parallel' or 'linear' -% options - Optional parameters +% [results, wh] = submitJobs(run_ids, dsp_options, submit_mode, submit_options) % -% Outputs: -% results - Cell array of results from each job -% wh - Updated DataStorage object - -% USAGE: -% % === SETTINGS === -% -% dsp_options.append_to_db = 1; -% dsp_options.max_occurences = 15; -% dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -% dsp_options.database_name = 'silas_labor_newdsp_newstructure.db'; -% dsp_options.storage_path = 'Z:\2024\sioe_labor\'; -% dsp_options.parameters = struct(); -% dsp_options.parameters.mu_dc = [0.005]; -% -% % === Get Run ID's === -% db = DBHandler("pathToDB", [dsp_options.database_path, dsp_options.database_name], "type", "sqlite"); -% fp = QueryFilter(); -% % fp.where('Runs', 'run_id','EQUALS', 5108); -% fp.where('Runs', 'is_mpi','EQUALS', 0); -% fp.where('Runs', 'fiber_length','EQUALS', 1); -% fp.where('Runs', 'wavelength','EQUALS', 1310); -% fp.where('Runs', 'db_mode','EQUALS', 0); -% fp.where('Runs', 'rop_attenuation','EQUALS', 0); -% fp.where('Runs', 'pam_level','EQUALS', 4); -% fp.where('Runs', 'bitrate','EQUALS', 360e9); -% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); -% [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); - -% % === Initialize DataStorage === -% wh = DataStorage(dsp_options.parameters); -% wh.addStorage("ffe_package"); -% wh.addStorage("mlse_package"); -% wh.addStorage("vnle_package"); -% wh.addStorage("dbtgt_package"); -% wh.addStorage("dbenc_package"); -% -% % === RUN IT === -% [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); -% wh.getStoValue('ffe_package',0.005); -% wh.getStoValue('mlse_package',0.005); - +% run_ids : scalar or vector of run IDs +% dsp_options : struct of dsp_runid name/value options +% submit_mode : processingMode.parallel or .serial +% submit_options : struct with fields +% .waitbar (logical) +% .wh (DataStorage object) +% +% results : cell(nJobsPerRunId, nRunIds) +% wh : updated DataStorage arguments - run_ids int32 = 0 - dsp_options struct = struct(); - submit_mode processingMode = processingMode.serial + run_ids int32 = 0 + dsp_options struct = struct() + submit_mode processingMode = processingMode.serial submit_options.waitbar (1,1) logical = true - submit_options.wh = DataStorage(struct()); % Optional DataStorage object + submit_options.wh = DataStorage(struct()) end -% Ensure run_ids is a row vector +% Normalize run_ids = run_ids(:)'; +nRunIds = numel(run_ids); +nJobsPerRunId = submit_options.wh.getLastLinIndice(); +totalJobs = nRunIds * nJobsPerRunId; -% Get number of jobs per run_id -nJobsPerRunId = submit_options.wh.getLastLinIndice; -nRunIds = length(run_ids); -totalJobs = nJobsPerRunId * nRunIds; - -% Initialize results array for all jobs +% Preallocate results = cell(nJobsPerRunId, nRunIds); -futures = cell(totalJobs, 1); +futures = parallel.FevalFuture.empty(totalJobs,0); +jobIndices = zeros(totalJobs,2); -% Initialize waitbar if requested +% Optional waitbar if submit_options.waitbar h = waitbar(0, 'Processing Jobs...'); cleanupObj = onCleanup(@() delete(h)); end -% Process jobs based on mode -if submit_mode == processingMode.parallel - setupParallelPool(); - - % Submit jobs to parallel pool +switch submit_mode + case processingMode.parallel + %—– SET UP POOL & QUEUE —– + p = setupParallelPool(11, 300); % 10 workers, 300s idle timeout + + % === submit all futures === jobCounter = 0; for r = 1:nRunIds for k = 1:nJobsPerRunId jobCounter = jobCounter + 1; - optionalVars = buildOptionalVars(k, submit_options.wh); - - % Submit job with proper arguments - futures{jobCounter} = parfeval(@dsp_runid, 1, run_ids(r), ... - "database_type", dsp_options.database_type,... - "dataBase", dsp_options.dataBase, ... - "append_to_db", dsp_options.append_to_db, ... + jobIndices(jobCounter,:) = [r,k]; + + opt = buildOptionalVars(k, submit_options.wh); + futures(jobCounter) = parfeval( ... + p, @dsp_runid, 1, ... + run_ids(r), ... + "database_type", dsp_options.database_type, ... + "dataBase", dsp_options.dataBase, ... + "append_to_db", dsp_options.append_to_db, ... "load_file_path", dsp_options.load_file_path, ... "max_occurences", dsp_options.max_occurences, ... - "storage_path", dsp_options.storage_path, ... - "mode", dsp_options.mode,... - "parameters", optionalVars); - + "storage_path", dsp_options.storage_path, ... + "mode", dsp_options.mode, ... + "parameters", opt ... + ); + fprintf('[RunID %d, Job %d] Submitted to pool.\n', run_ids(r), k); end end + %—– W A I T B A R U P D A T E —– if submit_options.waitbar - setupParallelWaitbar(futures, totalJobs, h); + futureArray = futures; + updateWB = @() waitbar( ... + sum(arrayfun(@(f) strcmp(f.State,'finished'), futureArray))/totalJobs, ... + h, sprintf('Completed %d/%d jobs', ... + sum(arrayfun(@(f) strcmp(f.State,'finished'), futureArray)), totalJobs) ... + ); + afterEach(futureArray, updateWB, 0); end - % Fetch results - jobCounter = 0; - for r = 1:nRunIds - for k = 1:nJobsPerRunId - jobCounter = jobCounter + 1; - try - results{k,r} = fetchOutputs(futures{jobCounter}); - fprintf('[RunID %d, Job %d] Completed successfully.\n', run_ids(r), k); - storeResult(results{k,r}, k, submit_options.wh); - catch ME - handleError(ME, k, run_ids(r)); - results{k,r} = ME; - end + % —– before the loop —– + % Keep track of which futures we've already handled: + consumedIdx = false(totalJobs,1); + + % —– fetch in completion order, handling successes and errors —– + for n = 1:totalJobs + try + % This returns the value AND the linear index in 'futures' + [idx, val] = fetchNext(futures); + + duration = futures(idx).RunningDuration; + startDT = futures(idx).StartDateTime; + finishDT = futures(idx).FinishDateTime; + duration = finishDT - startDT; + + % Mark it consumed + consumedIdx(idx) = true; + + % Map back to (r,k) and store + r = jobIndices(idx,1); + k = jobIndices(idx,2); + + fprintf('[%s] JobID %d/%d (%.1f%%) — %s — RunID %d — Subjob %d — fetched.\n', ... + datestr(now,'yyyy-mm-dd HH:MM:SS'), ... + r, totalJobs, 100*n/totalJobs,char(duration), ... + run_ids(r), k); + + % Update waitbar + if submit_options.waitbar + waitbar(n/totalJobs, h, ... + sprintf('Fetched %d/%d (%.1f% Percent)', n, totalJobs, 100*n/totalJobs)); + drawnow; % force the GUI to refresh end + + storeResult(val, k, submit_options.wh); + results{k,r} = val; + + catch fetchErr + % fetchNext has already set Read=true on the errored future. + % Find the one Read==true that we have _not_ yet consumed. + readMask = arrayfun(@(f) f.Read, futures); + idxErr = find(readMask & ~consumedIdx, 1); + consumedIdx(idxErr) = true; + + % Pull the _real_ exception out of the future object + errInfo = futures(idxErr).Error; + if iscell(errInfo) + origME = errInfo{1}; + else + origME = errInfo; + end + + % Map back to (r,k) and log + r = jobIndices(idxErr,1); + k = jobIndices(idxErr,2); + handleError(origME, k, run_ids(r)); + results{k,r} = origME; end - -elseif submit_mode == processingMode.serial - % Process jobs sequentially + end + + + case processingMode.serial + %—– SERIAL EXECUTION —– jobCounter = 0; for r = 1:nRunIds for k = 1:nJobsPerRunId jobCounter = jobCounter + 1; optionalVars = buildOptionalVars(k, submit_options.wh); - try fprintf('[RunID %d, Job %d] Running in linear mode...\n', run_ids(r), k); - - results{k,r} = dsp_runid(run_ids(r),... - "database_type", dsp_options.database_type,... - "dataBase", dsp_options.dataBase,... - "append_to_db", dsp_options.append_to_db,... - "load_file_path", dsp_options.load_file_path,... - "max_occurences", dsp_options.max_occurences,... - "storage_path", dsp_options.storage_path,... - "mode", dsp_options.mode,... - "parameters", optionalVars); + val = dsp_runid( run_ids(r), ... + "database_type", dsp_options.database_type, ... + "dataBase", dsp_options.dataBase, ... + "append_to_db", dsp_options.append_to_db, ... + "load_file_path", dsp_options.load_file_path, ... + "max_occurences", dsp_options.max_occurences, ... + "storage_path", dsp_options.storage_path, ... + "mode", dsp_options.mode, ... + "parameters", optionalVars ); fprintf('[RunID %d, Job %d] Completed successfully.\n', run_ids(r), k); - storeResult(results{k,r}, k, submit_options.wh); + storeResult(val, k, submit_options.wh); + results{k,r} = val; + catch ME handleError(ME, k, run_ids(r)); results{k,r} = ME; end - % Update waitbar if requested if submit_options.waitbar - waitbar(jobCounter/totalJobs, h, sprintf('Completed %d/%d jobs', jobCounter, totalJobs)); + waitbar(jobCounter/totalJobs, h, ... + sprintf('Completed %d/%d jobs', jobCounter, totalJobs)); end end end -else - error('') + + otherwise + error('Unknown submit_mode "%s".', string(submit_mode)) end wh = submit_options.wh; -% Helper functions remain the same except for handleError -% Modified handleError function to include run_id +%% Local helpers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + function handleError(ME, jobIndex, run_id) - fprintf('[RunID %d, Job %d] ERROR [%s]: %s\n', run_id, jobIndex, ME.identifier, ME.message); + fprintf('[RunID %d, Job %d] ERROR [%s]: %s\n', run_id, jobIndex, ... + ME.identifier, ME.message); for st = ME.stack' fprintf(' %s:%d (%s)\n', st.file, st.line, st.name); end fprintf('Full report:\n%s\n', getReport(ME,'extended')); end -% Other helper functions remain unchanged - function setupParallelPool() - curpool = gcp('nocreate'); - if isempty(curpool) - parpool(10,"IdleTimeout",300); - else - if ~isempty(curpool.FevalQueue.QueuedFutures) || ~isempty(curpool.FevalQueue.RunningFutures) - oldq = length(curpool.FevalQueue.QueuedFutures) + length(curpool.FevalQueue.RunningFutures); - curpool.FevalQueue.cancelAll; - fprintf('Canceled %d unfetched jobs from old queue.\n', oldq); - end - end - end - function optionalVars = buildOptionalVars(jobIndex, wh) optionalVars = struct(); - if ~isempty(wh.getDimension) - [parametervalues, parameternames] = wh.getPhysIndicesByLinIndex(jobIndex); - for pidx = 1:numel(parameternames) - optionalVars.(parameternames{pidx}) = parametervalues{pidx}; + if ~isempty(wh.getDimension()) + [vals, names] = wh.getPhysIndicesByLinIndex(jobIndex); + for pi = 1:numel(names) + optionalVars.(names{pi}) = vals{pi}; end end end - function setupParallelWaitbar(futures, totalJobs, h) - updateWaitbar = @(~) waitbar(sum(cellfun(@(f) strcmp(f.State, 'finished'), futures))/totalJobs, h, ... - sprintf('Completed %d/%d jobs', sum(cellfun(@(f) strcmp(f.State, 'finished'), futures)), totalJobs)); - - futureArray = [futures{:}]; - afterEach(futureArray, updateWaitbar, 0); - end - - function storeResult(result, jobIndex, wh) + function storeResult(val, jobIndex, wh) if ~isempty(wh) - wh.addValueToStorageByLinIdx(result.ffe_package, 'ffe_package', jobIndex); - wh.addValueToStorageByLinIdx(result.mlse_package, 'mlse_package', jobIndex); - wh.addValueToStorageByLinIdx(result.vnle_package, 'vnle_package', jobIndex); - wh.addValueToStorageByLinIdx(result.dbtgt_package, 'dbtgt_package', jobIndex); - wh.addValueToStorageByLinIdx(result.dbenc_package, 'dbenc_package', jobIndex); + wh.addValueToStorageByLinIdx(val.ffe_package, 'ffe_package', jobIndex); + wh.addValueToStorageByLinIdx(val.mlse_package, 'mlse_package', jobIndex); + wh.addValueToStorageByLinIdx(val.vnle_package, 'vnle_package', jobIndex); + wh.addValueToStorageByLinIdx(val.dbtgt_package,'dbtgt_package',jobIndex); + wh.addValueToStorageByLinIdx(val.dbenc_package,'dbenc_package',jobIndex); + end + end + function p = setupParallelPool(numWorkers, idleTimeout) + % Ensure a pool exists at the right size & timeout + p = gcp('nocreate'); + if isempty(p) || p.NumWorkers~=numWorkers + if ~isempty(p) + delete(p); + end + p = parpool('local', numWorkers, 'IdleTimeout', idleTimeout); + end + + % Cancel anything left in the pool's default queue + q = p.FevalQueue; + if ~isempty(q.QueuedFutures) || ~isempty(q.RunningFutures) + cancelAll(q); + fprintf('Canceled %d unfetched jobs from old queue.\n', ... + numel(q.QueuedFutures)+numel(q.RunningFutures)); end end -end \ No newline at end of file + +end 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 c73a50b..d34ec2c 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 = 1; +dsp_options.max_occurences = 15; experiment = "highspeed_2024"; dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' @@ -40,14 +40,14 @@ end fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); -% fp.where('Runs', 'pam_level','EQUALS', 4); -fp.where('Runs', 'bitrate','LESS_THAN', 310e9); -fp.where('Runs', 'fiber_length','EQUALS', 1); +fp.where('Runs', 'pam_level','EQUALS', 4); +% fp.where('Runs', 'bitrate','LESS_THAN', 310e9); +% fp.where('Runs', 'fiber_length','EQUALS', 1); 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', 1310); +% fp.where('Runs', 'wavelength','EQUALS', 1310); % fp.where('Runs', 'db_mode','EQUALS', 1); fp.where('Runs', 'rop_attenuation','EQUALS', 0); % fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); @@ -75,7 +75,7 @@ wh.addStorage("dbenc_package"); % === RUN IT === -[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true); +[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); % wh.getStoValue('ffe_package',0.005); From 09d9e5011c5fc0eda9d4892cba365969c88084d2 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Thu, 10 Jul 2025 13:32:25 +0200 Subject: [PATCH 06/30] re-attempt DB fetch connection --- Classes/DataBaseHandler/DBHandler.m | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index 73a4321..7e2105a 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -43,8 +43,8 @@ classdef DBHandler < handle elseif options.type == "mysql" % datasource = "jdbc:mysql://134.245.243.254:3306/labor"; - - + + obj.conn = database( ... string(obj.dataBase), ... % Database name @@ -578,10 +578,28 @@ classdef DBHandler < handle execute(obj.conn, query); end - function answer = fetch(obj,query) - answer = fetch(obj.conn,query); + + function answer = fetch(obj, query) + maxFast = 20; maxSlow = 30; + for attempt = 1:maxSlow + try + answer = fetch(obj.conn, query); + return + catch ME + if attempt < maxFast + pause(1) + else + pause(10) + end + lastErr = ME; + end + end + pause(60) + error('Database fetch failed after %d attempts:\n%s', maxSlow, lastErr.getReport()) end + + function [result,query] = queryDB(obj, filterParams, selectedFields) % getPathsWithFlexibleFilter Retrieves values from Runs table with flexible filtering % and lets the user select which fields to include in the SELECT statement. From 5dbc48abc0bc8886e2ac77bf7b6cd90fa769832e Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Mon, 11 Aug 2025 07:42:04 +0200 Subject: [PATCH 07/30] Many changes here and there. I lost track... :-( Current work is on MLSE and SD Decoding etc. MLSE is currently not 100% working, the scalings are maybe off?! --- Classes/00_signals/Signal.m | 44 ++-- Classes/01_transmit/M8199B.m | 2 +- Classes/01_transmit/PAMsource.m | 7 + Classes/02_optical/Fiber.m | 220 ++++++++-------- Classes/04_DSP/Sequence Detection/MLSE.m | 65 +++-- Classes/04_DSP/Sequence Detection/MLSE_new.m | 152 +++++++++++ Classes/DataBaseHandler/DBHandler.m | 4 +- Functions/EQ_structures/dsp_runid.m | 34 +-- Functions/EQ_structures/duobinary_target.m | 6 +- .../EQ_structures/vnle_postfilter_mlse.m | 17 +- Functions/EQ_visuals/showEQNoisePSD.m | 3 +- Functions/EQ_visuals/showEQfilter.m | 8 +- Functions/EQ_visuals/showLevelHistogram.m | 1 + .../loadAndSyncSignalDataFromDb.m | 2 +- Functions/Job_Processing/preprocessSignal.m | 6 +- Functions/Job_Processing/submitJobs.m | 2 +- Functions/Metrics/calc_air.m | 4 +- Functions/Metrics/calc_gmi_bitwise.m | 94 +++++++ Functions/Metrics/calc_ngmi.m | 32 +-- Functions/Metrics/calc_snr.m | 48 ++-- Functions/Theory/dispersion_contour.m | 54 ++++ Functions/getFigureSize.m | 33 +++ .../Auswertung_JLT/ber_vs_dispersion.m | 111 ++++++++ .../Auswertung_JLT/ber_vs_rate.m | 86 +++++++ .../ber_vs_rate_only_best_configs.m | 117 +++++++++ .../Auswertung_JLT/gmi_vs_rate.m | 84 +++++++ .../Auswertung_JLT/power_vs_wavelength.m | 96 +++++++ .../Auswertung_JLT/run_dsp_from_db.m | 14 +- .../Auswertung_JLT/simulate_and_dsp.m | 236 ++++++++++++++++++ .../auswertung/only_show_precomp.m | 7 +- .../bias_evaluation.m | 4 +- .../db_auswertung/rate_vs_ber.m | 6 +- projects/IMDD_base_system/imdd_model.m | 2 +- projects/IMDD_base_system/simulation_bwl.m | 234 +++++++++-------- projects/Job_Processing/run_offline_dsp.m | 117 --------- .../Job_Processing/run_offline_dsp_script.m | 116 --------- .../load_n_plot_transfer_characteristic.m | 8 + 37 files changed, 1506 insertions(+), 570 deletions(-) create mode 100644 Classes/04_DSP/Sequence Detection/MLSE_new.m create mode 100644 Functions/Metrics/calc_gmi_bitwise.m create mode 100644 Functions/Theory/dispersion_contour.m create mode 100644 Functions/getFigureSize.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_dispersion.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate_only_best_configs.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/gmi_vs_rate.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/power_vs_wavelength.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/simulate_and_dsp.m delete mode 100644 projects/Job_Processing/run_offline_dsp.m delete mode 100644 projects/Job_Processing/run_offline_dsp_script.m diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index eb41f39..9b0c3e5 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -269,6 +269,7 @@ classdef Signal SignalPower = [obj.power]; Nase = [0]; SignalCopy = obj.signal; + SignalCopy = []; ModifierName = class(CallingModifier); @@ -365,7 +366,7 @@ classdef Signal if options.normalizeTo0dB p_lin = p_lin./ max(p_lin); p_dbm = 10*log10(p_lin); % normalized to 0 dB - ylab = "normalized to 0 dB"; + ylab = "Normalized PSD"; else p_dbm = 10*log10(p_lin); ylab = "Power (dB/Hz)"; @@ -395,7 +396,7 @@ classdef Signal if options.normalizeToNyquist == 0 xlabel("Frequency in GHz"); edgetick = 2^(nextpow2(obj.fs*1e-9)); - xticks(-edgetick:16:edgetick); + xticks(-edgetick:32:edgetick); xlim([100*round(min(w)/100,1)-10, 100*round(max(w)/100,1)+10]) xlim([-128 128]);%256GSa/s else @@ -406,13 +407,20 @@ classdef Signal ylabel(ylab); try - ylim([max(min(floor(min(p_dbm))-3, ax.YLim(1)),-40), min(max(ceil(max(p_dbm))+3, ax.YLim(2)),10)]); + 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))-3, ceil(max(p_dbm))+3]); + ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]); end - ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]); + + if options.normalizeTo0dB + % ylim([-40, 3]); + ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]); + else + ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]); + end + yticks(-200:10:10); - grid on; grid minor; + grid on;% grid minor; legend % legend('Interpreter','none'); @@ -912,8 +920,8 @@ classdef Signal elseif mode == 1 % generate eye diagram using histogram - maxA = max(sig(100:end-100))*2; - minA = min(sig(100:end-100))*2; + maxA = max(sig(100:end-100))*1.3; + minA = min(sig(100:end-100))*1.3; % maxA = 0.0015; % minA = 0; @@ -926,8 +934,8 @@ classdef Signal nn=histcounts(data_ind_y(n,:),1:histpoints+1); hist_data(:,n)=flip(nn.'); %without flip, the eye is upside down :-( end - - ax = gca; + + plot_data = 20*log10(hist_data); plot_data(plot_data==-Inf) = 0; @@ -944,27 +952,29 @@ classdef Signal if isa(obj,'Opticalsignal') title(['Optical Eye ',options.displayname]) ylabel("Power in mW"); - y_tickstring = string(linspace(maxA.*1e3,minA.*1e3,16)); + y_tickstring = string(linspace(maxA.*1e3,minA.*1e3,6)); min_ = min(abs(obj.signal(100:end-100)).^2); max_ = abs(max(obj.signal(100:end-100)).^2); elseif isa(obj,'Electricalsignal') title(['Electrical Eye ',options.displayname]) ylabel("Voltage in V"); - y_tickstring = string(linspace(maxA,minA,16)); + y_tickstring = string(linspace(maxA,minA,6)); min_ = min(obj.signal(100:end-100)); max_ = abs(max(obj.signal(100:end-100))); else title(['Digital Eye ',options.displayname]) ylabel("Digital Signal Amplitude"); - y_tickstring = string(linspace(maxA,minA,16)); + y_tickstring = string(linspace(maxA,minA,6)); min_ = min(obj.signal(100:end-100)); max_ = abs(max(obj.signal(100:end-100))); end xlabel('Time in ps') + + % add information - if 0 + if 1 pwr_dbm = round(obj.power,3); pwr_lin = obj.power("unit",power_notation.W); @@ -1073,13 +1083,15 @@ classdef Signal end - yticks(linspace(0,histpoints,16)); + yticks(linspace(0,histpoints,6)); y_tickstring = sprintfc('%.2f', y_tickstring); yticklabels(y_tickstring); - xticks(linspace(0,histpoints_horizontal,8)) + xticks(linspace(0,histpoints_horizontal,6)) x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12); xticklabels(x_tickstring); + + grid off end diff --git a/Classes/01_transmit/M8199B.m b/Classes/01_transmit/M8199B.m index 2519453..7a2a34e 100644 --- a/Classes/01_transmit/M8199B.m +++ b/Classes/01_transmit/M8199B.m @@ -17,7 +17,7 @@ classdef M8199B < AWG fdac = 256e9; - Lp_awg = Filter('filtdegree',4,"f_cutoff",75e9,"fs",fdac*options.kover,"filterType",filtertypes.butterworth,"active",true); + Lp_awg = Filter('filtdegree',3,"f_cutoff",75e9,"fs",fdac*options.kover,"filterType",filtertypes.gaussian,"active",true); obj = obj@AWG("fdac",fdac,"dac_min",dac_min,"dac_max",dac_max,"lpf_active",1,"H_lpf",Lp_awg,"kover",options.kover,... "bit_resolution",5.5,"normalize2dac",1,"upsampling_method","samplehold"); diff --git a/Classes/01_transmit/PAMsource.m b/Classes/01_transmit/PAMsource.m index dd77f89..d29f7f7 100644 --- a/Classes/01_transmit/PAMsource.m +++ b/Classes/01_transmit/PAMsource.m @@ -136,6 +136,13 @@ classdef PAMsource %%%%%% Duobinary %%%%%%%%%%% + if obj.db_precode + obj.duobinary_mode = db_mode.db_precoded; + end + if obj.db_encode + obj.duobinary_mode = db_mode.db_encoded; + end + switch obj.duobinary_mode case db_mode.no_db diff --git a/Classes/02_optical/Fiber.m b/Classes/02_optical/Fiber.m index 55f8ec2..fbdca1b 100644 --- a/Classes/02_optical/Fiber.m +++ b/Classes/02_optical/Fiber.m @@ -1,189 +1,171 @@ classdef Fiber - %FIBER Summary of this class goes here - % Detailed explanation goes here + % Fiber: Simulate optical fiber signal propagation using + % the split-step Fourier method (SSFM). properties + % Simulation sampling frequency [Hz] fsimu + % Fiber length [m] fiber_length + % Attenuation coefficient [dB/km] alpha + % Dispersion parameter D [s/m^2] D + % Dispersion slope [s/m^3] Dslope + % Reference wavelength [m] lambda0 + % Nonlinear coefficient [1/(W·m)] gamma + % Maximum allowed nonlinear phase change per step [rad] dphimax + % Derived parameters: + % Second-order dispersion coefficient β2 [s^2/m] b2 + % Third-order dispersion coefficient β3 [s^3/m] b3 + % Linear attenuation constant [1/m] alpha_lin + % Frequency-domain linear operator per unit length linstep end methods function obj = Fiber(options) - %FIBER Construct an instance of this class - % Detailed explanation goes here + % Constructor: initialize fiber physical and simulation parameters. arguments - options.fsimu - options.fiber_length = 0 - options.alpha = 0.2 - options.D = 17 - options.Dslope = 0.06 - options.lambda0 = 1550 - options.gamma = 0 - options.dphimax = 5e-3 + options.fsimu % Sampling frequency [Hz] + options.fiber_length = 0 % Fiber length [km] + options.alpha = 0.2 % Attenuation [dB/km] + options.D = 17 % Dispersion D parameter [ps/(nm·km)] + options.Dslope = 0.06 % Dispersion slope [ps/(nm^2·km)] + options.lambda0 = 1550 % Reference wavelength [nm] + options.gamma = 0 % Nonlinear coefficient [1/(W·km)] + options.dphimax = 5e-3 % Max phase step [rad] end - obj.fsimu = options.fsimu; - obj.fiber_length = options.fiber_length*1000; %km - obj.alpha = options.alpha; - obj.D = options.D*1e-6; - obj.Dslope = options.Dslope*1e3; - obj.lambda0 = options.lambda0*1e-9; - obj.gamma = options.gamma; - obj.dphimax = options.dphimax; - - - + % Assign inputs to object properties, converting units to SI. + obj.fsimu = options.fsimu; + obj.fiber_length = options.fiber_length * 1e3; % km → m + obj.alpha = options.alpha; + obj.D = options.D * 1e-6; % ps/(nm·km) → s/(m·m) + obj.Dslope = options.Dslope * 1e3; % ps/(nm^2·km) → s/m^2 + obj.lambda0 = options.lambda0 * 1e-9; % nm → m + obj.gamma = options.gamma; % 1/(W·km) (assumed → 1/(W·m) internally) + obj.dphimax = options.dphimax; end - function signalclass_out = process(obj,signalclass_in) + function signalclass_out = process(obj, signalclass_in) + % process: Apply fiber propagation to input signal class. + % Calls the internal SSFM routine and logs the operation. - % actual processing of the signal (steps 1. - 3.) - signalclass_in = obj.process_(signalclass_in); + % Run internal split-step propagation + signalclass = obj.process_(signalclass_in); - % append to logbook - lbdesc = 'Fiber '; - signalclass_in = signalclass_in.logbookentry(lbdesc); - - % write to output - signalclass_out = signalclass_in; + % Add entry to logbook for tracking + signalclass = signalclass.logbookentry('Fiber '); + % Return processed signal class + signalclass_out = signalclass; end - function opt_sig = process_(obj,opt_sig) - %METHOD1 Summary of this method goes here - % Detailed explanation goes here + function opt_sig = process_(obj, opt_sig) + % process_: Internal routine for one-step fiber propagation. + % Computes linear and (optionally) nonlinear effects. - signal = opt_sig.signal; + % Extract time-domain field and wavelength + signal = opt_sig.signal; lambda_signal = opt_sig.lambda; - obj.D = obj.D + (lambda_signal-obj.lambda0)*obj.Dslope; - - obj.b2 = -obj.D*lambda_signal^2/(2*pi*Constant.LightSpeed); - obj.b3 = ((lambda_signal.^2/(2*pi*Constant.LightSpeed)).^2*obj.Dslope); - obj.alpha_lin = obj.alpha/10*log(10)/1000; - + % Update dispersion parameter D for current wavelength + obj.D = obj.D + (lambda_signal - obj.lambda0) * obj.Dslope; + + % Compute dispersion coefficients (β2, β3) + obj.b2 = -obj.D * lambda_signal^2 / (2 * pi * Constant.LightSpeed); + obj.b3 = (lambda_signal^2 / (2 * pi * Constant.LightSpeed))^2 * obj.Dslope; + + % Convert attenuation from dB/km to linear 1/m + obj.alpha_lin = obj.alpha / 10 * log(10) / 1e3; + + % Build frequency axis for FFT operations N = length(signal); - faxis = linspace(-obj.fsimu/2,obj.fsimu/2,N+1); - faxis = ifftshift(faxis(:,1:end-1)); - faxis = faxis'; - - obj.linstep = -obj.alpha_lin/2 - 2*1j*pi^2*obj.b2*faxis.^2 - 4/3*1j*pi^3*obj.b3*faxis.^3; + faxis = linspace(-obj.fsimu/2, obj.fsimu/2, N+1); + faxis = faxis(1:end-1); % drop redundant endpoint + faxis = ifftshift(faxis)'; % center zero frequency - if 0 - H = exp((obj.linstep)*obj.fiber_length); - - figure(222) - hold on - plot(faxis.*1e-9,abs(real(Y)),'LineStyle','-','DisplayName','Abs(real) part of complex TF'); - xlabel("Frequency in GHz") - ylabel("$ R|(H(\omega, L))|$") - end + % Define linear operator per meter in frequency domain + obj.linstep = -obj.alpha_lin/2 ... % half-step loss + - 1j*2*pi^2*obj.b2 .* faxis.^2 ... % second-order dispersion + - 1j*(4/3)*pi^3*obj.b3 .* faxis.^3; % third-order dispersion + % Choose linear-only or nonlinear SSFM based on gamma if obj.gamma ~= 0 + % Full adaptive SSFM with nonlinear Schrödinger solver opt_out = obj.NLSE(signal); else - opt_out = ifft( fft(signal) .* exp(obj.linstep*obj.fiber_length) ); % only one linear step + % Single-step linear propagation in frequency domain + opt_out = ifft( fft(signal) .* exp(obj.linstep * obj.fiber_length) ); end - %TODO: attenuate nase ... - + % Update output field in signal class opt_sig.signal = opt_out; - end - function [yout] = NLSE(obj, xin) - - maxPow = obj.gamma.*max(abs(xin).^2); - - Leff = obj.dphimax / maxPow ; - - dz = Leff; + function yout = NLSE(obj, xin) + % NLSE: Solve nonlinear Schrödinger equation by adaptive split-step + % xin: input time-domain field + % Returns yout: output time-domain field after propagation + % Initial estimate of effective length per step + maxPow = obj.gamma * max(abs(xin).^2); + Leff = obj.dphimax / maxPow; + dz = Leff; z_prop = 0; - yout = fft(xin); - - yout = ((yout).*exp(obj.linstep*dz/2)); + % Apply initial half-step linear operator + yout = fft(xin) .* exp(obj.linstep * dz/2); + % Loop until full fiber length is reached while true - + % Inverse FFT to time domain for nonlinear phase shift yout = ifft(yout); - Leff = dz; - + % Nonlinear phase rotation per segment power = abs(yout).^2; - Hnl = exp( -1j*obj.gamma*power*Leff); - yout = yout .* Hnl; + Hnl = exp(-1j * obj.gamma * power * dz); + yout = yout .* Hnl; + % Accumulate propagation distance z_prop = z_prop + dz; - maxPow = obj.gamma*max(abs(yout).^2); - Leff = obj.dphimax/maxPow; - - dz_new = Leff; + % Compute adaptive step for next interval + maxPow = obj.gamma * max(abs(yout).^2); + dz_new = obj.dphimax / maxPow; + % If remaining length shorter than new step, finish loop if z_prop + dz_new > obj.fiber_length dz_new = obj.fiber_length - z_prop; - break + break; end - yout = fft(yout); - - yout = ((yout).*exp(obj.linstep*(dz/2+dz_new/2))); - - dz = dz_new; - + % Half-step linear operator bridging segments + yout = fft(yout) .* exp(obj.linstep * ((dz/2) + (dz_new/2))); + dz = dz_new; end - yout = fft(yout); - - yout = ((yout).*exp(obj.linstep*(dz/2+dz_new/2))); - + % Final propagation segment: combine half-steps and nonlinear + yout = fft(yout) .* exp(obj.linstep * ((dz/2) + (dz_new/2))); yout = ifft(yout); - Leff = dz; - + % Last nonlinear phase shift power = abs(yout).^2; + Hnl = exp(-1j * obj.gamma * power * dz_new); + yout = yout .* Hnl; - Hnl = exp( -1j*obj.gamma*power*Leff); - - yout = yout .* Hnl; - - yout = fft(yout); - - yout = ((yout).*exp(obj.linstep*(dz/2))); - + % Final half-step linear operator to complete SSFM + yout = fft(yout) .* exp(obj.linstep * (dz_new/2)); yout = ifft(yout); - - end - - end end - - - - - - - - - - - - - - - diff --git a/Classes/04_DSP/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index c453e18..e9d99dc 100644 --- a/Classes/04_DSP/Sequence Detection/MLSE.m +++ b/Classes/04_DSP/Sequence Detection/MLSE.m @@ -83,6 +83,14 @@ classdef MLSE < handle % impulse respnse to remove from signal obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000 + % % make the combined impulse-response have net gain = 1 + % h = obj.DIR(:); + % h = h / sum(h); + % obj.DIR = h.'; + + % Normalize the Trellis states to =1 RMS + obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states); + % 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); @@ -116,6 +124,7 @@ classdef MLSE < handle % first: RMS normalization of input data (rms==1) data_in = data_in ./ rms(data_in); + data_in = data_in - mean(data_in); % then, match amplitude levels of input signal to those of the calculated ideal symbols % i.e. match the rms values of data_in to noise_free_received (rms=1.xx) @@ -125,6 +134,10 @@ classdef MLSE < handle end end + y_clean = conv( data_ref, flip(obj.DIR), "same" ); + sigma2 = var( data_in - y_clean ); + inv2s2 = 1/(2*sigma2); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% FORWARD PASS (VITERBI -Alpha's) %%%%% @@ -134,7 +147,7 @@ classdef MLSE < handle % first start is evaluated without ISI/ wihout the full Impulse response % so simply use the constellation here - bm = -(data_in(1) - last_sym).^2 ; + 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); @@ -143,10 +156,10 @@ classdef MLSE < handle % Forward Recursion (FSM Computation) for n = 2:length(data_in) - bm = -(data_in(n) - noise_free_received).^2 ; + bm = -(data_in(n) - noise_free_received).^2 * inv2s2; pm = pm + bm; - [alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as new state - pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state) + [alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as new state + pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state) bm_fw(:,:,n) = bm; @@ -177,7 +190,7 @@ classdef MLSE < handle % predecessor for h = length(data_in)-1:-1:1 - bm = -(data_in(h+1) - noise_free_received).^2 ; + bm = -(data_in(h+1) - noise_free_received).^2 * inv2s2; 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) @@ -214,10 +227,22 @@ classdef MLSE < handle %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% FORWARD PASS PAM2,4,8 %%%%% - nml_LLP = LLP - max(LLP); - expLLP = exp(nml_LLP); %subtract highst value for better numerical stability, LLP's are not always close to zero - state_prob = expLLP ./ sum(expLLP); + 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 + + % figure + % hold on; + % for i = 1:obj.M + % scatter(1:length(expLLP),expLLP(i,:),1,'.'); + % end + % scatter(1:length(expLLP),max(expLLP(:,:)),3,'.'); if obj.M == 6 @@ -305,8 +330,8 @@ classdef MLSE < handle % 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).demap(first_sym./rms(first_sym)); - + % bit_mapping = PAMmapper(length(obj.trellis_states),0,"eth_style",0).demap(first_sym./rms(first_sym)); + 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); @@ -343,8 +368,8 @@ classdef MLSE < handle llr1 = LLR_exact(idx_bit_0,k); % Calculate mutual information for bit position k - I0 = mean(log2(1 + exp(llr0(100:end-100)))); % exp(--LLR) = exp(positive) > 1 - I1 = mean(log2(1 + exp(-llr1(100:end-100)))); % exp(-+LLR) = exp(negative) < 1 + 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 @@ -354,8 +379,7 @@ classdef MLSE < handle VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS); - - debug = 0; + debug = 1; if debug %%% DEBUG PLOT LIKELIHOOD RATIOS %%% figure(115);clf @@ -380,6 +404,7 @@ classdef MLSE < handle if debug tx_bits = reshape(tx_bits',[],1); + disp('Start DEBUG MLSE:') % DECIDE based on Viterbi traceback VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS); rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(VITERBI_ESTIMATION_SYMBOLS'); @@ -417,13 +442,19 @@ classdef MLSE < handle rx_bits = reshape(rx_bits',[],1); [~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); fprintf('FW BER: %.2e \n',ber_fw); - + disp('Stop DEBUG MLSE:') + disp('') end end - end - methods (Access=private) + 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/04_DSP/Sequence Detection/MLSE_new.m b/Classes/04_DSP/Sequence Detection/MLSE_new.m new file mode 100644 index 0000000..ef7a1e2 --- /dev/null +++ b/Classes/04_DSP/Sequence Detection/MLSE_new.m @@ -0,0 +1,152 @@ +classdef MLSE_new < handle + %MLSE: BCJR‐based soft‐output for PAM‐M sequence estimation & GMI + + properties + M % PAM order (e.g. 4) + DIR % channel impulse response + trellis_states % PAM constellation levels (e.g. [-3 -1 1 3]) + end + + methods + function obj = MLSE_new(opts) + arguments + opts.M double = 4; + opts.DIR double = 1; + opts.trellis_states double = [-3 -1 1 3]; + end + obj.M = opts.M; + obj.DIR = opts.DIR; + obj.trellis_states = opts.trellis_states; + end + + function [hd_out, LLR, NGMI] = process(obj, signalclass,ref_symbolclass) + % rx, tx: column vectors of equalized and reference symbols + data_in = signalclass.signal; + shape_in = size(data_in); + data_ref = ref_symbolclass.signal; + + [data_out_hd,LLR,GMI] = obj.bcjr_soft(data_in,data_ref); + try + data_out_hd = reshape(data_out_hd,shape_in(1),shape_in(2)); + catch + warning('output reshaping failed after MLSE'); + end + + signalclass_hd = signalclass; + signalclass_hd.signal = data_out_hd; + + + + + [hd_out, LLR, NGMI] = obj.bcjr_soft(rx, tx); + end + end + + methods (Access=private) + function [hd_sym, LLR, NGMI] = bcjr_soft(obj, y, x) + %--- build trellis states & branch outputs --- + DIR = flip(obj.DIR(:)); + S = combvec(obj.trellis_states, obj.trellis_states)'; + prev = S(:,1); cur = S(:,2); + branch_out = prev*DIR(1) + cur*DIR(2); % for 2‐tap + + N = numel(y); + M = obj.M; + m = log2(M); + + %--- forward/backward metrics (max‐log) --- + nStates = numel(obj.trellis_states); + alpha = -inf(nStates,N); + beta = -inf(nStates,N); + bm_fw = zeros(nStates,nStates,N); + + % branch metrics + sigma2 = var(y - x); + inv2sigma2 = 1/(2*sigma2); + for n=1:N + bm = -((y(n)-branch_out).^2)* inv2sigma2; + bm = reshape(bm, nStates, nStates); + bm_fw(:,:,n) = bm; + end + % forward + alpha(:,1) = max(bm_fw(:,:,1),[],2); + for n=2:N + mat = alpha(:,n-1) + bm_fw(:,:,n); + alpha(:,n) = max(mat,[],2); + end + % backward + beta(:,N) = 0; + for n=N-1:-1:1 + mat = beta(:,n+1).' + bm_fw(:,:,n+1); + beta(:,n) = max(mat,[],2); + end + + % Precompute the “zero‐th” forward metric + alpha0 = zeros(nStates,1); + + LLP = -inf(nStates,nStates,N); + for n = 1:N + % Select the correct previous alpha column + if n == 1 + a_prev = alpha0; + else + a_prev = alpha(:,n-1); + end + + % Combine α, branch metric, and β + mat = a_prev + bm_fw(:,:,n) + beta(:,n).'; + LLP(:,:,n) = mat; + end + + %--- compute LLRs symbol‐wise via log‐sum‐exp over branches --- + levels = obj.trellis_states; + bit_map = PAMmapper(M,0).demap(levels'); + LLR = zeros(N,m); + for n=1:N + % sum branch posteriors per symbol + llp_n = LLP(:,:,n); + logZ = logsumexp(llp_n(:)); + post = exp(llp_n - logZ); + % aggregate per symbol index + Psym = zeros(M,1); + for i=1:nStates + for j=1:nStates + % branch (i->j) emits symbol 'cur(j)' + idx = find(levels==cur(j)); + Psym(idx) = Psym(idx) + post(i,j); + end + end + % bit‐LLR from symbol posterior + for k=1:m + idx1 = bit_map(:,k)==1; + idx0 = ~idx1; + P1 = sum(Psym(idx1)); + P0 = sum(Psym(idx0)); + LLR(n,k) = log(P1/P0); + end + end + + %--- hard decisions --- + [~,symIdx] = max(LLR,[],2); + hd_sym = levels(symIdx); + + %--- GMI via Alvarado Eq.(30) --- + tx_bits = PAMmapper(M,0).demap(x); + MI = zeros(1,m); + for k=1:m + r0 = LLR(tx_bits(:,k)==0,k); + r1 = LLR(tx_bits(:,k)==1,k); + I0 = mean(log2(1+exp(-r0))); + I1 = mean(log2(1+exp(+r1))); + MI(k) = 1 - 0.5*(I0+I1); + end + GMI = sum(MI); + NGMI = GMI/m; + end + end +end + +function s = logsumexp(a) + m = max(a(:)); + s = m + log(sum(exp(a-m), 'all')); +end diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index 7e2105a..d21d3be 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -44,8 +44,6 @@ classdef DBHandler < handle % datasource = "jdbc:mysql://134.245.243.254:3306/labor"; - - obj.conn = database( ... string(obj.dataBase), ... % Database name "silas", ... % Username @@ -645,7 +643,7 @@ classdef DBHandler < handle cleanedTable.(varNames{i}) = numCol; else % Clean double-quoted SQL literals (e.g., ""no_db"") - cleanedTable.(varNames{i}) = strrep(string(col), '""', '"'); + cleanedTable.(varNames{i}) = strrep(string(col), '"', ''); end end end diff --git a/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m index 50dfc46..a698314 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -26,13 +26,15 @@ try % Initialize database connection database = DBHandler("dataBase", [options.dataBase], "type", options.database_type ); - % 2. Check if an equalizer configuration with the same hash exists - queryStr = sprintf('SELECT COUNT(DISTINCT eq_id) AS unique_eq_count, COUNT(*) AS entries_for_run FROM `Results` WHERE run_id = %d', run_id); - existing_results = database.fetch(queryStr); - - if existing_results.unique_eq_count >= 6 - if (existing_results.entries_for_run / existing_results.unique_eq_count) > 8 - return + if 0 + % 2. Check if an equalizer configuration with the same hash exists + queryStr = sprintf('SELECT COUNT(DISTINCT eq_id) AS unique_eq_count, COUNT(*) AS entries_for_run FROM `Results` WHERE run_id = %d', run_id); + existing_results = database.fetch(queryStr); + + if existing_results.unique_eq_count >= 6 + if (existing_results.entries_for_run / existing_results.unique_eq_count) > 5 + return + end end end @@ -94,8 +96,8 @@ try adaption= 1; use_dd_mode = 1; - use_ffe = 1; - use_dfe = 1; + use_ffe = 0; + use_dfe = 0; use_vnle_mlse = 1; use_dbtgt = 1; use_dbenc = 1; @@ -139,6 +141,10 @@ try % Preprocess signal Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym); + % Scpe_sig.spectrum("fignum",2223,"normalizeTo0dB",1,"displayname",'Rx'); + % Scpe_sig.spectrum("fignum",22233,"normalizeTo0dB",0,"displayname",'Rx'); + % Scpe_sig.eye(fsym,M,"fignum",1024); + if duob_mode ~= db_mode.db_encoded if use_ffe @@ -146,19 +152,19 @@ try ffe_order = [50, 0, 0]; eq_dfe = 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); - dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... + ffe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... "precode_mode",duob_mode,... 'showAnalysis',0,... "postFFE",[],... "eth_style_symbol_mapping",0); - output.ffe_package{r} = dfe_results; + output.ffe_package{r} = ffe_results; - dfe_results.metrics.print; - dfe_results.config.equalizer_structure = "ffe"; + ffe_results.metrics.print; + ffe_results.config.equalizer_structure = "ffe"; if options.append_to_db - database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config); + database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config); end end diff --git a/Functions/EQ_structures/duobinary_target.m b/Functions/EQ_structures/duobinary_target.m index 22257d7..687d6ca 100644 --- a/Functions/EQ_structures/duobinary_target.m +++ b/Functions/EQ_structures/duobinary_target.m @@ -110,12 +110,14 @@ if options.showAnalysis rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum"); - Duobinary().encode(tx_symbols).spectrum("normalizeTo0dB",1,"fignum",250,"displayname","DB encoded reference"); + Duobinary().encode(tx_symbols).spectrum("normalizeTo0dB",1,"fignum",10,"displayname","DB encoded reference"); showEQNoisePSD(eq_noise,"fignum",250,"displayname",'Duobinary Target Noise after Equalization'); - fprintf('DB tgt BER: %.2e \n',ber); + fprintf('DB tgt BER: %.2e \n',ber_db); + figure(341); clf; + showLevelHistogram(eq_signal, db_ref_sequence, "fignum", 341); end diff --git a/Functions/EQ_structures/vnle_postfilter_mlse.m b/Functions/EQ_structures/vnle_postfilter_mlse.m index 6610221..2ad8ea2 100644 --- a/Functions/EQ_structures/vnle_postfilter_mlse.m +++ b/Functions/EQ_structures/vnle_postfilter_mlse.m @@ -43,6 +43,7 @@ end % Hard decision on VNLE output eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); +eq_signal_hd.spectrum("displayname",'after full response FFE','fignum',2025,'normalizeTo0dB',1); % Process through postfilter and MLSE [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); @@ -216,10 +217,24 @@ end function displayAnalysis(eq_noise, whitened_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, postFFE) + +% rx_signal.spectrum("displayname",'Rx Signal','fignum',100,'normalizeTo0dB',1); + % Display analysis plots and metrics 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); +for t = 1:4 + pf_.ncoeff = t; + [~,~] = pf_.process(eq_signal_sd, eq_noise); + showEQNoisePSD(eq_noise, "fignum", 3388, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients); +end + + +tx_symbols.spectrum("displayname",'Equalized Signal','fignum',1234,'normalizeTo0dB',1); if ~isempty(postFFE) showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338); end @@ -234,7 +249,7 @@ showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 341); warning off showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400); -showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401); +% showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401); drawnow; warning on diff --git a/Functions/EQ_visuals/showEQNoisePSD.m b/Functions/EQ_visuals/showEQNoisePSD.m index a10fb15..64f002d 100644 --- a/Functions/EQ_visuals/showEQNoisePSD.m +++ b/Functions/EQ_visuals/showEQNoisePSD.m @@ -22,7 +22,7 @@ end % Ensure the figure is ready before calling spectrum eq_noise.spectrum("displayname", options.displayname, "fignum", fig.Number, "normalizeTo0dB", 1,"color",options.color); - title('Noise of soft decision signal (not MLSE)') + title('EEN') if ~isnan(options.postfilter_taps) % Hold on to the figure for further plotting @@ -43,5 +43,6 @@ end end xlim([-eq_noise.fs/2* 1e-9 eq_noise.fs/2* 1e-9]); + ylim([-15, 0]); end diff --git a/Functions/EQ_visuals/showEQfilter.m b/Functions/EQ_visuals/showEQfilter.m index 3e0f4b0..4b0f403 100644 --- a/Functions/EQ_visuals/showEQfilter.m +++ b/Functions/EQ_visuals/showEQfilter.m @@ -20,6 +20,12 @@ end f = f(1:half_nfft); H = H(1:half_nfft); + H_mag = abs(H); + H_norm = H_mag ./ max(H_mag); + + H_db = 20*log10(H_norm); + H_db = H_db - min(H_db); + % Determine the figure number to use or create a new figure if isnan(options.fignum) fig = figure; % Create a new figure and get its handle @@ -30,7 +36,7 @@ end % Magnitude response (in dB) subplot(2,1,1); hold on - plot(f.*1e-9, 20*log10(abs(1./H)),'DisplayName',options.displayname); + plot(f.*1e-9, H_db,'DisplayName',options.displayname); title('(Inverted) Magnitude Response of FFE Filter'); xlabel('Frequency (GHz)'); ylabel('Magnitude (dB)'); diff --git a/Functions/EQ_visuals/showLevelHistogram.m b/Functions/EQ_visuals/showLevelHistogram.m index 0f31bdc..ef88791 100644 --- a/Functions/EQ_visuals/showLevelHistogram.m +++ b/Functions/EQ_visuals/showLevelHistogram.m @@ -27,6 +27,7 @@ end received_sd = NaN(numel(constellation),length(ref_symbols)); lvlcol = cbrewer2('Paired',numel(constellation)*2); lvlcol = lvlcol(2:2:end,:); + lvlcol = linspecer(numel(constellation)); % lvlcol = cbrewer2('Set1',numel(constellation)); for lvl = 1:numel(constellation) %Separate the equalized signal into the diff --git a/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m b/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m index db2812c..1b76fd4 100644 --- a/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m +++ b/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m @@ -1,7 +1,7 @@ function [Bits, Symbols, Scpe_cell, found_sync] = loadAndSyncSignalDataFromDb(dataTable, options) % LOADSIGNALDATA Loads and synchronizes signal data from storage % -% Inputs: +% Inputs:d % dataTable - Table with file paths and configuration % options - Struct with storage_path and max_occurences % diff --git a/Functions/Job_Processing/preprocessSignal.m b/Functions/Job_Processing/preprocessSignal.m index 4828bba..8725ed3 100644 --- a/Functions/Job_Processing/preprocessSignal.m +++ b/Functions/Job_Processing/preprocessSignal.m @@ -16,9 +16,9 @@ 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); +% Scpe_sig = Filter('filtdegree', 4, "f_cutoff", Symbols.fs.*0.6, ... +% "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ... +% "active", true).process(Scpe_sig); % Remove DC offset Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); diff --git a/Functions/Job_Processing/submitJobs.m b/Functions/Job_Processing/submitJobs.m index d667cc8..321032a 100644 --- a/Functions/Job_Processing/submitJobs.m +++ b/Functions/Job_Processing/submitJobs.m @@ -120,7 +120,7 @@ switch submit_mode % fetchNext has already set Read=true on the errored future. % Find the one Read==true that we have _not_ yet consumed. readMask = arrayfun(@(f) f.Read, futures); - idxErr = find(readMask & ~consumedIdx, 1); + idxErr = find(readMask & ~consumedIdx', 1); consumedIdx(idxErr) = true; % Pull the _real_ exception out of the future object diff --git a/Functions/Metrics/calc_air.m b/Functions/Metrics/calc_air.m index c42ab2f..2b10827 100644 --- a/Functions/Metrics/calc_air.m +++ b/Functions/Metrics/calc_air.m @@ -1,5 +1,5 @@ function [ach_inf_rate] = calc_air(test_signal,reference_signal,options) -% Calculation of AIR acc. to J. Kozesnik, „Numerically Computing Achievable Rates of Memoryless Channels“, Francisco Javier Garcıa-Gomez, doi: 10.1007/978-94-009-9857-5. +% „Numerically Computing Achievable Rates of Memoryless Channels“, Francisco Javier Garcıa-Gomez, doi: 10.1007/978-94-009-9857-5. % Implementation is not accessible, I mailed TUM to get the code... arguments(Input) @@ -26,7 +26,7 @@ end % TRIM [test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end); -% CALC EVM +% CALC AIR %%% new implementation of AIR constellation = unique(reference_signal); reference_idx = arrayfun(@(x) find(constellation == x, 1), reference_signal); diff --git a/Functions/Metrics/calc_gmi_bitwise.m b/Functions/Metrics/calc_gmi_bitwise.m new file mode 100644 index 0000000..1ce8847 --- /dev/null +++ b/Functions/Metrics/calc_gmi_bitwise.m @@ -0,0 +1,94 @@ +function [GMI,NGMI] = calc_gmi_bitwise(test_signal,reference_signal,options) +% https://cioffi-group.stanford.edu/doc/book/AppendixG.pdf + +arguments(Input) + test_signal; + reference_signal; + options.skip_front = 0; + options.skip_end = 0; + options.returnErrorLocation = 0; +end + +options.skip_end = abs(options.skip_end); +options.skip_front = abs(options.skip_front); + +assert((options.skip_end+options.skip_front); +% % Retrieve current size of the active (or any) figure: +% [wCur, hCur] = getFigureSize(); +% % Set the other figure H to match that size, preserving its position: +% posH = get(H, 'Position'); % [left, bottom, width, height] +% newPos = [posH(1), posH(2), wCur, hCur]; +% set(H, 'Position', newPos); +% +% Note: +% - Position vector is given as [left, bottom, width, height] in pixels. +% - If you want to specify a custom size directly, you can replace wCur/hCur +% with desired values. + +function [width, height] = getFigureSize() + % Ensure a figure is available + fig = gcf; + % Get the position vector: [left, bottom, width, height] + pos = get(fig, 'Position'); + % Extract width and height + width = pos(3); + height = pos(4); +end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_dispersion.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_dispersion.m new file mode 100644 index 0000000..447daac --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_dispersion.m @@ -0,0 +1,111 @@ + +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); + +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 987); +M = 6; +fp.where('Runs', 'pam_level','EQUALS', M); +baudrate = 162e9; +fp.where('Runs', 'symbolrate','EQUALS', baudrate); +% fp.where('Runs', 'fiber_length','EQUALS', 2); +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', 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')]; +[dataTable,~] = db.queryDB(fp, fields); + +eqstructures = unique(dataTable.equalizer_structure); +fiber_len = unique(dataTable.fiber_length); +cnt = 1; +f=figure(); +clf +hold on + +markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'}; % Define marker styles + +for fl = 1:numel(fiber_len) + + fl_filtered = dataTable(dataTable.fiber_length == fiber_len(fl),:); + + for eqs = [equalizer_structure.vnle_pf_mlse] + + eq_choice = equalizer_structure(eqs); + if sum(eqstructures == eq_choice)~=1 + disp(eq_choice) + continue + end + + eq_filtered = fl_filtered(fl_filtered.equalizer_structure == eq_choice,:); + + dispersion_sorted = sortrows(eq_filtered, {'accumulated_dispersion'}, 'ascend'); + % dispersion_sorted = dispersion_sorted(dispersion_sorted.wavelength <= 1320,:); + % dispersion_sorted = dispersion_sorted(dispersion_sorted.BER < 0.02,:); + % pull out your vectors + accumulated_dispersion = dispersion_sorted.accumulated_dispersion; + ber = dispersion_sorted.BER; + % ber = dispersion_sorted.BER_precoded; + run_ids = dispersion_sorted.run_id; % <-- this is what we want in the datatip + len = dispersion_sorted.fiber_length; + lambda = dispersion_sorted.wavelength; + cols = cbrewer2('Set1',8); + % cols = flip(cbrewer2('RdYlGn',14)); + cols = linspecer(8); + + ber_wavelen_grouped = groupsummary( ... + dispersion_sorted, ... % input table + "wavelength", ... % grouping variable + "min", ... % which summary statistic + "BER_precoded"); + + dname = sprintf('%s; %d km',eq_choice, fiber_len(fl)); + h1 = plot(ber_wavelen_grouped.wavelength, ber_wavelen_grouped.min_BER_precoded,'LineWidth', 2, 'MarkerSize', 5,'Marker',markers(cnt),'LineStyle','-','Color',cols(cnt,:),'MarkerEdgeColor','auto','MarkerFaceColor','white','DisplayName',dname); + + + plotallscatters=0; + if plotallscatters + % plot the two curves and capture their Line handles + dname = sprintf('%s; %d km',eq_choice, fiber_len(fl)); + h1 = plot(lambda, ber,'LineWidth', 1.5, 'MarkerSize', 5,'Marker','o','LineStyle','none','Color',cols(cnt,:),'MarkerFaceColor',cols(cnt,:),'DisplayName',dname); + % —————— Add run_id as a datatip row —————— + % For each line, tell the datatip template where to find the run_id: + h1.DataTipTemplate.DataTipRows(end+1) = ... + dataTipTextRow('run\_id', run_ids); + h1.DataTipTemplate.DataTipRows(end+1) = ... + dataTipTextRow('len', len); + h1.DataTipTemplate.DataTipRows(end+1) = ... + dataTipTextRow('lambda', lambda); + end + + xticks(sort(unique(lambda))); + xticklabels(sort(unique(lambda))); + + grid on; + + % Labels, scales, legend, etc. + xlabel('Wavelength in nm','FontSize',12); + ylabel('BER','FontSize',12); + tit = sprintf('%d GBd PAM-%d',baudrate.*1e-9, M); + title(tit,'FontSize',14,'FontWeight','bold'); + set(gca, 'XScale','linear','YScale','log','FontSize',11); + legend + + xlim([min(lambda)-2, max(lambda)+2]); + ylim([1e-4, 0.2]); + + cnt = cnt+1; + end +end + +yline([4.85e-3, 2e-2],'--','LineWidth',1,'HandleVisibility','off'); +posH = get(f, 'Position'); % [left, bottom, width, height] +newPos = [posH(1), posH(2), 750, 300]; +set(f, 'Position', newPos); \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m new file mode 100644 index 0000000..7039713 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m @@ -0,0 +1,86 @@ + +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); + +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 987); +M = 8; +fp.where('Runs', 'pam_level','EQUALS', M); +% fp.where('Runs', 'bitrate','LESS_THAN', 310e9); +fp.where('Runs', 'fiber_length','EQUALS', 2); +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', 1310); +% fp.where('Runs', 'db_mode','EQUALS', 1); +fp.where('Runs', 'rop_attenuation','EQUALS', 0); + +% [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard')); + +eqstructures = unique(dataTable.equalizer_structure); +% Create the figure +figure(10); +hold on +for pre_emph = [1] + + dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:); + + for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse , equalizer_structure.vnle_db_mlse] + + eq_choice = equalizer_structure(eqs); + + if sum(eqstructures == eq_choice)~=1 + disp(eq_choice) + continue + end + + eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:); + + symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend'); + [~, ia] = unique(symbolrate_sorted.symbolrate, 'first'); + symbolrate_sorted = symbolrate_sorted(ia, :); + + % Example data (replace these with your real vectors) + symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud + bitrate = symbolrate * floor(log2(M)*10)/10; + ber = symbolrate_sorted.min_BER; % BER + ber_precoded = symbolrate_sorted.min_BER_precoded; % BER + cols = cbrewer2('Paired',12); + cols = [0.4660 0.6740 0.1880 ; 0.9290 0.6940 0.1250 ; 0 0.4470 0.7410; 0.4940 0.1840 0.5560]; %VNLE; PF ; DFE ; DB tgt + + dname = [char(eq_choice)]; + if pre_emph + dname = [dname,' with pre-emph.']; + else + dname = [dname,' w/o pre-emph.']; + end + + plot(bitrate, ber, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','o','LineStyle','-','Color',cols((2*eqs)+1+pre_emph,:),'MarkerEdgeColor',cols((2*eqs)+1+pre_emph,:),'MarkerFaceColor',[1,1,1],'DisplayName',[dname]); + plot(bitrate, ber_precoded, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','square','LineStyle',':','Color',cols((2*eqs)+1+pre_emph,:),'MarkerEdgeColor',cols((2*eqs)+1+pre_emph,:),'MarkerFaceColor',[1,1,1],'DisplayName',[dname,'; pre-coded']); + grid on; + + % Axis labels and title + xlabel('Baud Rate GBaud', 'FontSize', 12); + ylabel('BER', 'FontSize', 12); + title('BER vs. Baud Rate', 'FontSize', 14, 'FontWeight', 'bold'); + + % Improve tick formatting + set(gca, 'XScale', 'linear', ... + 'YScale', 'log', ... + 'TickLabelInterpreter', 'latex', ... + 'FontSize', 11); + legend + + xticks(bitrate); + + % Optional: tighten axis limits + xlim([min(bitrate), max(bitrate)]); + ylim([5e-5, 0.5]); + + end + +end + +yline([4.85e-3, 2e-2],'LineWidth',1,'LineStyle','--','HandleVisibility','off'); beautifyBERplot(); \ No newline at end of file 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 new file mode 100644 index 0000000..58ef10d --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate_only_best_configs.m @@ -0,0 +1,117 @@ + +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); + +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 987); +M = 8; +fp.where('Runs', 'pam_level','EQUALS', M); +% fp.where('Runs', 'bitrate','LESS_THAN', 310e9); +fp.where('Runs', 'fiber_length','EQUALS', 2); +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', 1310); +% fp.where('Runs', 'db_mode','EQUALS', 1); +fp.where('Runs', 'rop_attenuation','EQUALS', 0); + +[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard')); + +eqstructures = unique(dataTable.equalizer_structure); + +% Create the figure +f=figure(4); +hold on + +eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse , equalizer_structure.vnle_db_mlse]; +cols = [0.4660 0.6740 0.1880 ; 0.9290 0.6940 0.1250 ; 0 0.4470 0.7410; 0.4940 0.1840 0.5560]; %VNLE; PF ; DFE ; DB tgt + +eq_choice = equalizer_structure.vnle; +pre_emph = 1; +dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:); +eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:); +symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend'); +[~, ia] = unique(symbolrate_sorted.symbolrate, 'first'); +symbolrate_sorted = symbolrate_sorted(ia, :); +symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud +bitrate = symbolrate * floor(log2(M)*10)/10; +ber = symbolrate_sorted.min_BER; % BER +ber_precoded = symbolrate_sorted.min_BER_precoded; % BER +plot(bitrate, ber, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','v','LineStyle',':','Color',cols(1,:),'MarkerEdgeColor',cols(1,:),'MarkerFaceColor',cols(1,:),'DisplayName',['Tx pre-emphasis + VNLE']); + +eq_choice = equalizer_structure.vnle_pf_mlse; +pre_emph = 0; +dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:); +eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:); +symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend'); +[~, ia] = unique(symbolrate_sorted.symbolrate, 'first'); +symbolrate_sorted = symbolrate_sorted(ia, :); +symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud +bitrate = symbolrate * floor(log2(M)*10)/10; +ber = symbolrate_sorted.min_BER; % BER +ber_precoded = symbolrate_sorted.min_BER_precoded; % BER +plot(bitrate, ber, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','diamond','LineStyle',':','Color',cols(2,:),'MarkerEdgeColor',cols(2,:),'MarkerFaceColor',cols(2,:),'DisplayName',['VNLE+2-tap post-filter+MLSE']); + +% eq_choice = equalizer_structure.dfe; +% pre_emph = 1; +% dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:); +% eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:); +% symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend'); +% [~, ia] = unique(symbolrate_sorted.symbolrate, 'first'); +% symbolrate_sorted = symbolrate_sorted(ia, :); +% symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud +% bitrate = symbolrate * floor(log2(M)*10)/10; +% ber = symbolrate_sorted.min_BER; % BER +% ber_precoded = symbolrate_sorted.min_BER_precoded; % BER +% plot(bitrate, ber, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','o','LineStyle','-','Color',cols(3,:),'MarkerEdgeColor',cols(3,:),'MarkerFaceColor',cols(3,:),'DisplayName',[char(eq_choice)]); + +eq_choice = equalizer_structure.vnle_db_mlse; +pre_emph = 0; +dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:); +eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:); +symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend'); +[~, ia] = unique(symbolrate_sorted.symbolrate, 'first'); +symbolrate_sorted = symbolrate_sorted(ia, :); +symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud +bitrate = symbolrate * floor(log2(M)*10)/10; +ber = symbolrate_sorted.min_BER; % BER +ber_precoded = symbolrate_sorted.min_BER_precoded; % BER +plot(bitrate, ber_precoded, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','square','LineStyle',':','Color',cols(4,:),'MarkerEdgeColor',cols(4,:),'MarkerFaceColor',cols(4,:),'DisplayName',['DB precoding + DB tgt. + MLSE']); + + +% Axis labels and title with Arial font +xlabel('Gross bitrate [Gb/s]', 'FontSize', 12, 'FontName', 'Arial', 'Interpreter', 'none'); +ylabel('BER', 'FontSize', 12, 'FontName', 'Arial', 'Interpreter', 'none'); +title('', 'FontSize', 14, 'FontWeight', 'bold', 'FontName', 'Arial', 'Interpreter', 'none'); + +% Improve tick formatting +set(gca, 'XScale', 'linear', ... + 'YScale', 'log', ... + 'TickLabelInterpreter', 'none', ... + 'FontSize', 11, ... + 'FontName', 'Arial'); + +% Legend with Arial font +% legend('FontName', 'Arial', 'Interpreter', 'none','Location','best'); + +xticks(bitrate); + +% Optional: tighten axis limits +xlim([min(bitrate), max(bitrate)]); +ylim([5e-4, 0.05]); + +yline([3.8e-3], 'LineWidth', 2, 'LineStyle', '--', ... + 'HandleVisibility', 'off', 'LabelHorizontalAlignment', 'left'); + +posH = get(f, 'Position'); % [left, bottom, width, height] +newPos = [posH(1), posH(2), 350, 200]; +set(f, 'Position', newPos); + +annotation(f,'textbox',... + [0.398095238095238 0.273381294964029 0.491428571428572 0.140287769784173],... + 'String',{'PAM-8; 2 km; 1293 nm'},... + 'LineWidth',0.5,... + 'FitBoxToText','off',... + 'BackgroundColor',[1 1 1]); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/gmi_vs_rate.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/gmi_vs_rate.m new file mode 100644 index 0000000..7f84f62 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/gmi_vs_rate.m @@ -0,0 +1,84 @@ + +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); +fp.where('Runs', 'fiber_length','EQUALS', 2); +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', 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')); + +eqstructures = unique(dataTable.equalizer_structure); + +% Create the figure +figure(18); +hold on + +for pre_emph = [0,1] + + dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:); + + for eqs = [equalizer_structure.vnle] + + eq_choice = equalizer_structure(eqs); + if sum(eqstructures == eq_choice)~=1 + disp(eq_choice) + continue + end + + eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:); + if eqs ==equalizer_structure.vnle_pf_mlse + 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 + cols = cbrewer2('Paired',12); + + dname = [char(eq_choice)]; + dname = strrep(dname,'_','+'); + if pre_emph + dname = [dname,'; w/ pre-emph.']; + else + dname = [dname,'; w/o pre-emph.']; + end + + plot(symbolrate, snr, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','o','LineStyle','-','Color',cols((2*eqs)+1+pre_emph,:),'MarkerEdgeColor',cols((2*eqs)+1+pre_emph,:),'MarkerFaceColor',[1,1,1],'DisplayName',[dname]); + grid on; + + % Axis labels and title + xlabel('Bit Rate Gbps', 'FontSize', 12); + ylabel('GMI', 'FontSize', 12); + title('GMI vs. Bit Rate', 'FontSize', 14, 'FontWeight', 'bold'); + + % Improve tick formatting + set(gca, 'XScale', 'linear', ... + 'YScale', 'linear', ... + 'TickLabelInterpreter', 'none', ... + 'FontSize', 11); + legend + + xticks(symbolrate); + + % Optional: tighten axis limits + xlim([min(symbolrate), max(symbolrate)]); + % ylim([log2(M)-1, log2(M)]); + + end + +end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/power_vs_wavelength.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/power_vs_wavelength.m new file mode 100644 index 0000000..d018b47 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/power_vs_wavelength.m @@ -0,0 +1,96 @@ + +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); + +fp = QueryFilter(); +fp.where('power_state_info', 'pam_level','EQUALS', 4); +fp.where('power_state_info', 'db_mode','EQUALS', 1); +% fp.where('power_state_info', 'fiber_length','EQUALS', 1); +fp.where('power_state_info', 'is_mpi','EQUALS', 0); + +fields = db.getTableFieldNames('power_state_info'); +% [dataTable,~] = db.queryDB(fp, fields); + +fiber_len = unique(dataTable.fiber_length); +cnt = 0; + +y_variable = 'power_mzm'; +x_variable = "wavelength"; +f = figure(3); +clf +hold on +for fl = 1:numel(fiber_len) + + + fl_filtered = dataTable(dataTable.fiber_length == fiber_len(fl),:); + [~, ia] = unique(fl_filtered.run_id, 'first'); + fl_filtered = fl_filtered(ia, :); + + fl_filtered_ = groupsummary( ... + fl_filtered, ... % input table + x_variable, ... % grouping variable + "mean", ... % which summary statistic + y_variable); % which column to average + + wavelength_sorted = sortrows(fl_filtered, {'wavelength'}, 'ascend'); + + % pull out your vectors + lambda = wavelength_sorted.wavelength; + power_laser = wavelength_sorted.power_laser; + power_mzm = wavelength_sorted.power_mzm; + power_rop = wavelength_sorted.power_rop; + power_pd = wavelength_sorted.power_pd_in; + voa = wavelength_sorted.voa_atten; + len = wavelength_sorted.fiber_length; + run_ids = wavelength_sorted.run_id; % <-- this is what we want in the datatip + cols = linspecer(8); + + + % plot the two curves and capture their Line handles + % h1 = plot(lambda, power_laser,'LineWidth', 0.5, 'MarkerSize', 4,'Marker','o','LineStyle','none','Color',cols(fl,:),'MarkerFaceColor',cols(fl,:),'DisplayName','Laser Output'); + % % —————— Add run_id as a datatip row —————— + % % For each line, tell the datatip template where to find the run_id: + % h1.DataTipTemplate.DataTipRows(end+1) = ... + % dataTipTextRow('run\_id', run_ids); + % h1.DataTipTemplate.DataTipRows(end+1) = ... + % dataTipTextRow('len', run_ids); + % h1.DataTipTemplate.DataTipRows(end+1) = ... + % dataTipTextRow('voaatten', voa); + + % + dname = sprintf('%s; %d km',y_variable, fiber_len(fl)); + h2 = plot(fl_filtered_.(x_variable), fl_filtered_.(['mean_',y_variable]), 'LineWidth', 1, 'MarkerSize', 4,'Marker','o','LineStyle','-','Color',cols(fl,:),'MarkerFaceColor',cols(fl,:),'DisplayName',dname); + + h2.DataTipTemplate.DataTipRows(end+1) = ... + dataTipTextRow('run\_id', run_ids); + h2.DataTipTemplate.DataTipRows(end+1) = ... + dataTipTextRow('len', len); + h2.DataTipTemplate.DataTipRows(end+1) = ... + dataTipTextRow('voaatten', voa); + + grid on; + xticks(sort(unique(lambda))); + xticklabels(sort(unique(lambda))); + + % Labels, scales, legend, etc. + xlabel('Wavelength in nm','FontSize',12); + ylabel('Power in dB','FontSize',12); + title('Power ','FontSize',14,'FontWeight','bold'); + set(gca, 'XScale','linear','YScale','linear','FontSize',11); + legend + + xlim([min(lambda)-2, max(lambda)+2]); + ylim([floor(min(fl_filtered_.(['mean_',y_variable])))-1 12]); + ylim([-12 12]); + + cnt = cnt+1; + + yline(8,'HandleVisibility','off'); + +end + +yline([4.85e-3, 2e-2],'--','LineWidth',1,'HandleVisibility','off'); +posH = get(f, 'Position'); % [left, bottom, width, height] +newPos = [posH(1), posH(2), 750, 300]; +set(f, 'Position', newPos); \ No newline at end of file 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 d34ec2c..ad75339 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.append_to_db = 0; +dsp_options.max_occurences = 1; experiment = "highspeed_2024"; dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' @@ -41,14 +41,14 @@ end fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); fp.where('Runs', 'pam_level','EQUALS', 4); -% fp.where('Runs', 'bitrate','LESS_THAN', 310e9); -% fp.where('Runs', 'fiber_length','EQUALS', 1); +fp.where('Runs', 'bitrate','EQUALS', 360e9); +fp.where('Runs', 'fiber_length','EQUALS', 2); 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', 1310); -% fp.where('Runs', 'db_mode','EQUALS', 1); +fp.where('Runs', 'wavelength','EQUALS', 1310); +fp.where('Runs', 'db_mode','EQUALS', 0); fp.where('Runs', 'rop_attenuation','EQUALS', 0); % fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); @@ -75,7 +75,7 @@ wh.addStorage("dbenc_package"); % === RUN IT === -[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); +[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true); % wh.getStoValue('ffe_package',0.005); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/simulate_and_dsp.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/simulate_and_dsp.m new file mode 100644 index 0000000..772f110 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/simulate_and_dsp.m @@ -0,0 +1,236 @@ + +precomp_mode = 0; +precomp_path = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\HighSpeedExperiment_2024\Auswertung_JLT"; +precomp_fn = "precomp_simulated.mat"; + +% TX +M = 4; +fsym = 72e9; +f_nyquist = fsym/2; +apply_pulsef = 1; +fdac = 256e9; +fadc = 256e9; +% fdac = 2*fsym; +% fadc = 2*fsym; +random_key = 1; + +duob_mode = db_mode.no_db; + +tx_bwl = 0.8.*f_nyquist; +rx_bwl = 0.8.*f_nyquist; + +rcalpha = 0.05; +kover = 16; +vbias_rel = 0.5; +u_pi = 2.9; +vbias = -vbias_rel*u_pi; +laser_wavelength = 1293; +laser_linewidth = 0; + + +% Channel +link_length = 60000; + +% RX +rop = -8; + +% EQ +eq_mode = equalizer_structure.vnle_pf_mlse; +ffe_order=[50,0,0]; +vnle_order=[50,5,5]; +dfe_order = [0 0 0]; + +len_tr = 4096*2; +mu_ffe = [0.0004 0.0004 0.0004]; +mu_dfe = 0.0004; +mu_dc = 0.00; + +dfe_ = sum(dfe_order)>0; + +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); + +[Digi_sig,Symbols,Tx_bits] = PAMsource(... + "fsym",fsym,"M",M,"order",18,"useprbs",0,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.5,... + "applypulseform",apply_pulsef,"pulseformer",Pform,... + "randkey",random_key,... + "duobinary_mode",duob_mode).process(); + +if precomp_mode == 1 % measure channel + precomp_est = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',fdac); + Digi_sig = precomp_est.buildOFDM(); +elseif precomp_mode == 2 % apply precomp + precomp_est = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',Digi_sig.fs); + Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',-50,'loadPath',precomp_path,'fileName',precomp_fn); +end + +% Symbols.spectrum("displayname",'Tx Symbols','fignum',10,'normalizeTo0dB',1); + +Digi_sig.eye(fsym,M,"fignum",1234567); +%%%%% AWG +%El_sig = M8199B("kover",kover).process(Digi_sig); +El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); +% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); +% El_sig = El_sig.setPower(0,"dBm"); +El_sig.spectrum("displayname",'Tx Signal','fignum',10,'normalizeTo0dB',0); +%%%%% Low-pass el. components %%%%%% + +El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.gaussian,"active",true).process(El_sig); +% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); + +%%%%% Electrical Driver Amplifier %%%%%% +El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); +El_sig = El_sig.normalize("mode","oneone"); + +%%%%% MODULATE E/O CONVERSION %%%%%% +[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); + +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 %%%%%% +Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); + +%%%%%% PD Square Law %%%%%% +Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig); + +%%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% +Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.bessel_inp,"active",true).process(Rx_sig); + +% %%%%%% Low-pass Scope %%%%%% +Lp_scpe = Filter('filtdegree',4,"f_cutoff",35e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + +% Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); + +%%%%%% 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(Rx_sig); + +Scpe_sig.spectrum("displayname",'Rx Signal','fignum',10,'normalizeTo0dB',1); +Scpe_sig.eye(fsym,M,"fignum",1973763) + +%%%%% Precompensation Routine %%%%%% +if precomp_mode == 1 + Scpe_sig_resampled = Scpe_sig.resample("fs_in",fadc,"fs_out",2*fsym); + precomp_est.estimate(Scpe_sig_resampled,"save",false,"savePath",precomp_path,"fileName",precomp_fn); + precomp_est.plot(); + precomp_est.save(); +end + +% Preprocess signal +Scpe_sig = preprocessSignal(Scpe_sig, Symbols, fsym); +Scpe_sig.signal = Scpe_sig.signal(1:2*Symbols.length); +use_ffe = 0; +use_dfe = 0; +use_vnle_mlse = 1; +use_dbtgt = 1; +use_dbenc = 1; + + +if duob_mode ~= db_mode.db_encoded + + if use_ffe + + ffe_order = [50, 0, 0]; + eq_dfe = 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_dfe,M,Scpe_sig,Symbols,Tx_bits,... + "precode_mode",duob_mode,... + 'showAnalysis',1,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + + disp('FFE:') + ffe_results.metrics.print; + + + end + + if use_dfe + + ffe_order = [50, 5, 5]; + eq_dfe = EQ("Ne",ffe_order,"Nb",[2,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); + + dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,... + "precode_mode",duob_mode,... + 'showAnalysis',0,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + + disp('DFE:') + dfe_results.metrics.print; + + + end + + if use_vnle_mlse + + if 0 + pf_ncoeffs = 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",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', 1, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + disp('VNLE:') + ffe_results.metrics.print; + disp('MLSE:') + mlse_results.metrics.print; + 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', 1, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + disp('VNLE:') + ffe_results.metrics.print; + disp('MLSE:') + mlse_results.metrics.print; + + + end + + if use_dbtgt + 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); + + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); + + dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, ... + 'showAnalysis',1 ,... + "postFFE", []); + + disp('DB:') + dbt_results.metrics.print; + + end +end + +if duob_mode == db_mode.db_encoded + + eq_db_enc = 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); + + 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",1,"postFFE",[]); + + db_results.metrics.print; +end \ 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 9902b03..2a8ee51 100644 --- a/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m +++ b/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m @@ -5,7 +5,7 @@ precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active db_precode = 0; db_coding_approach = 0; -fsym = 224e9; +fsym = 160e9; fdac = 256e9; random_key = 0; M = 4; @@ -58,8 +58,7 @@ end rcalpha = 0.05; -Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rcalpha); - +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); Pamsource = PAMsource(... "fsym",fsym,"M",M,"order",19,"useprbs",1,... @@ -83,7 +82,9 @@ Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',precomp_amp_max,'loadPath',pr 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); diff --git a/projects/HighSpeedExperiment_2024/bias_evaluation.m b/projects/HighSpeedExperiment_2024/bias_evaluation.m index babd881..3fe8d9d 100644 --- a/projects/HighSpeedExperiment_2024/bias_evaluation.m +++ b/projects/HighSpeedExperiment_2024/bias_evaluation.m @@ -1,5 +1,5 @@ -filename = "C:\Users\sioe\Documents\High_Speed_Measurement_2024\bias_5km\PAMX_5km_20241025_204334_wh.mat"; +filename = "Z:\2024\sioe\High Speed Messungen Oktober\bias_5km\PAMX_5km_20241025_204334_wh.mat"; a = load(filename); wh = a.obj; @@ -95,7 +95,7 @@ end -filename = "C:\Users\sioe\Documents\High_Speed_Measurement_2024\bias_testing_and_b2b\PAM4_b2b_bias_sweep_20241023_191202_wh_BB_BIAS_FINAL.mat"; +filename = "Z:\2024\sioe\High Speed Messungen Oktober\bias_testing_and_b2b\PAM4_b2b_bias_sweep_20241023_191202_wh_BB_BIAS_FINAL.mat"; a = load(filename); wh = a.obj; diff --git a/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m b/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m index a42bad8..9e527cf 100644 --- a/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m +++ b/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m @@ -1,12 +1,12 @@ basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -database = DBHandler("pathToDB",[basePath,'silas_labor.db'],"type",'sqlite'); +database = DBHandler("dataBase",[basePath,'silas_labor.db'],"type",'sqlite'); filterParams = database.tables; filterParams.Configurations = struct( ... - 'bitrate', [], ... %[224,336,360,390,420,448] - 'db_mode', [], ... + 'bitrate', [390e9], ... %[224,336,360,390,420,448] + 'db_mode', 1, ... 'fiber_length', 1, ... 'interference_attenuation', [], ... 'interference_path_length', [], ... diff --git a/projects/IMDD_base_system/imdd_model.m b/projects/IMDD_base_system/imdd_model.m index 70b7b29..d400350 100644 --- a/projects/IMDD_base_system/imdd_model.m +++ b/projects/IMDD_base_system/imdd_model.m @@ -1,6 +1,6 @@ function [output] = imdd_model(varargin) -simulation_mode = 0; +simulation_mode = 1; %%% Change folder curFolder = pwd; diff --git a/projects/IMDD_base_system/simulation_bwl.m b/projects/IMDD_base_system/simulation_bwl.m index e1477a7..f47b0aa 100644 --- a/projects/IMDD_base_system/simulation_bwl.m +++ b/projects/IMDD_base_system/simulation_bwl.m @@ -1,33 +1,24 @@ %%% Run parameters % TX M = 4; -fsym = 180e9; +fsym = 112e9; apply_pulsef = 1; fdac = 256e9; fadc = 256e9; -random_key = 1; - -db_precode = 0; -db_encode = 0; +random_key = 2; rcalpha = 0.05; kover = 16; vbias_rel = 0.5; u_pi = 2.9; vbias = -vbias_rel*u_pi; -laser_wavelength = 1310; +laser_wavelength = 1290; laser_linewidth = 0; -tx_bw_nyquist = 0.6; -rx_bw_nyquist = 0.6; % Channel -link_length = 1; - -% RX -rop = -8; - +link_length = 10000; vnle_order1 = 50; vnle_order2 = 3; @@ -49,55 +40,13 @@ mu_dc = 0.005; mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; mu_dfe = 0.0004; - dfe_ = sum(dfe_order)>0; doub_mode = db_mode.no_db; - -Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"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,"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(); - -Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); - - -%%%%% AWG -% El_sig = M8199A("kover",kover).process(Digi_sig); -El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); -% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); -% El_sig = El_sig.setPower(0,"dBm"); - -%%%%% Low-pass el. components %%%%%% -f_nyquist = fsym/2; -tx_bwl = tx_bw_nyquist.*f_nyquist; -% tx_bwl = 80e9; -El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); -% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); - -%%%%% Electrical Driver Amplifier %%%%%% -El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); -El_sig = El_sig.normalize("mode","oneone"); - -%%%%% MODULATE E/O CONVERSION %%%%%% -[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); - -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 = [-10]; +rop = [-8]; +bwl = [0.5:0.1:1.5]; +fsym = [128:16:170].*1e9; ber_vnle = []; ber_mlse = []; ber_viterbi = []; @@ -107,71 +56,121 @@ gmi_vnle = []; gmi_mlse = []; gmi_mlse_db = []; -for r = 1:length(rop) +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(); + + % Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',0); + + + %%%%% AWG + % El_sig = M8199A("kover",kover).process(Digi_sig); + El_sig = AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); + % El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); + % El_sig = El_sig.setPower(0,"dBm"); + + %%%%% Low-pass el. components %%%%%% + % f_nyquist = fsym/2; + % tx_bwl = tx_bw_nyquist.*f_nyquist; + tx_bwl = 50e9; + El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); + % El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); + + %%%%% Electrical Driver Amplifier %%%%%% + El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); + El_sig = El_sig.normalize("mode","oneone"); + + %%%%% MODULATE E/O CONVERSION %%%%%% + [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); + + 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 %%%%%% - Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop(r)).process(Opt_sig); + Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); %%%%%% PD Square Law %%%%%% Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig); %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% - rx_bwl = rx_bw_nyquist.*f_nyquist; - % rx_bwl = 80e9; + % rx_bwl = rx_bw_nyquist.*f_nyquist; + rx_bwl = 50e9; Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(Rx_sig); % %%%%%% Low-pass Scope %%%%%% Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); % Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); - + % %%%%%% 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(Rx_sig); + "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(Rx_sig); - Scpe_sig_resampled = Scpe_sig.resample("fs_out",2*fsym); - - [~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + Scpe_sig_resampled = 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_resampled.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 1); + % Scpe_cell = cell(1); + % Scpe_cell{1} = Scpe_sig_resampled; + + Scpe_cell{1}.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); 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); mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); - %Duobinary Targeting if 0 - db_ref_sequence = Duobinary().encode(Symbols); - db_ref_constellation = unique(db_ref_sequence.signal); - [eq_signal, eq_noise] = eq_.process(Scpe_cell{1},db_ref_sequence); - - mlse_.DIR = [1,1]; - [mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); - - mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(mlse_sig_sd); - 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),~] = 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",0).demap(mlse_sig_hd); - [bits_mlse,errors_mlse,ber_db(r),~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + %Duobinary Targeting + db_ref_sequence = Duobinary().encode(Symbols); + db_ref_constellation = unique(db_ref_sequence.signal); + [eq_signal, eq_noise] = eq_.process(Scpe_cell{1},db_ref_sequence); + + mlse_.DIR = [1,1]; + [mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); + + mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(mlse_sig_sd); + 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),~] = 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",0).demap(mlse_sig_hd); + [bits_mlse,errors_mlse,ber_db(r),~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); end % FFE or VNLE 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); [eq_signal_sd, eq_noise] = eq_.process(Scpe_cell{1}, Symbols); - [gmi_vnle(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100); + [gmi_gomez(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100); + [gmi_vnle(r)] = calc_ngmi(eq_signal_sd,Symbols); + [gmi_bitwise(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',118); + % Hard decision on VNLE output eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); @@ -180,19 +179,21 @@ for r = 1:length(rop) fprintf('BER VNLE: %.2e \n',ber_vnle(r)); fprintf('NGMI VNLE: %.2f \n',gmi_vnle(r)./log2(M)); - + if 1 + % Process through postfilter and MLSE pf_ncoeffs = 1; pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); mlse_ = MLSE("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; + 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); [~,~,ber_mlse(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); fprintf('BER: %.2e \n',ber_mlse(r)); - fprintf('NGMI MLSE: %.2f \n',gmi_mlse(r)./log2(M)); + fprintf('GMI MLSE: %.5f \n',gmi_mlse(r)); % Process through postfilter and MLSE @@ -205,23 +206,48 @@ for r = 1:length(rop) 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('FW BER: %.2e \n',ber_viterbi(r)); + fprintf('Viterbi BER: %.2e \n',ber_viterbi(r)); + end + + end +cols = cbrewer2('Paired',8); +d = 1; -figure();hold on -plot(rop,gmi_mlse,'DisplayName','MLSE','Marker','*'); -plot(rop,gmi_vnle,'DisplayName','VNLE','Marker','*'); -plot(rop,gmi_mlse_db,'DisplayName','MLSE DB Tgt','Marker','*'); +figure(11);hold on +plot(fsym.*1e-9,alpha,'DisplayName','VNLE','Marker','x','LineStyle','-','Color',cols(1+d,:)); +xlabel('Baudrate in GBd'); +ylabel('alpha'); + +figure(15);hold on +plot(fsym.*1e-9,gmi_gomez,'DisplayName','gomez','Marker','x','LineStyle','-','Color',cols(1+d,:)); +plot(fsym.*1e-9,gmi_vnle,'DisplayName','vnle other','Marker','x','LineStyle','-','Color',cols(3+d,:)); +plot(fsym.*1e-9,gmi_bitwise,'DisplayName','bitwise','Marker','x','LineStyle','--','Color',cols(5+d,:)); +plot(fsym.*1e-9,gmi_mlse,'DisplayName','MLSE','Marker','*'); ylim([0 2.6]); +xlabel('Baudrate in GBd'); +ylabel('GMI'); -figure();hold on -plot(rop,ber_vnle,'DisplayName','VNLE'); -plot(rop,ber_mlse,'DisplayName','MLSE','Marker','*'); -plot(rop,ber_viterbi,'DisplayName','Viterbi','Marker','*'); -plot(rop,ber_db_diff_precoded,'DisplayName','MLSE db diff','Marker','*'); -plot(rop,ber_db,'DisplayName','MLSE db','Marker','*'); +figure(13);hold on +plot(fsym.*1e-9,ber_vnle,'DisplayName','VNLE','Marker','x','LineStyle','-','Color',cols(1+d,:)); +plot(fsym.*1e-9,ber_mlse,'DisplayName','MLSE','Marker','x','LineStyle','-','Color',cols(3+d,:)); +plot(fsym.*1e-9,ber_viterbi,'DisplayName','Viterbi','Marker','x','LineStyle','--','Color',cols(5+d,:)); +% plot(bwl,ber_db_diff_precoded,'DisplayName','MLSE db diff','Marker','.','MarkerSize',15,'LineStyle','-'); +% plot(bwl,ber_db,'DisplayName','MLSE db','Marker','.','MarkerSize',15,'LineStyle','-'); +xlabel('Baudrate in GBd'); +ylabel('BER'); set(gca, 'yscale', 'log'); +% ylim([1e-6 0.1]); legend + + + +% 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))); +end diff --git a/projects/Job_Processing/run_offline_dsp.m b/projects/Job_Processing/run_offline_dsp.m deleted file mode 100644 index 335b57b..0000000 --- a/projects/Job_Processing/run_offline_dsp.m +++ /dev/null @@ -1,117 +0,0 @@ -% === SETTINGS === - -dsp_options.append_to_db = 1; -dsp_options.max_occurences = 15; -dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -dsp_options.database_name = 'silas_labor_newdsp_newstructure.db'; -dsp_options.storage_path = 'Z:\2024\sioe_labor\'; -dsp_options.parameters = struct(); -dsp_options.parameters.mu_dc = [0.005]; - -% === Get Run ID's === -db = DBHandler("pathToDB", [dsp_options.database_path, dsp_options.database_name], "type", "sqlite"); -fp = QueryFilter(); -% fp.where('Runs', 'run_id','EQUALS', 5108); -fp.where('Runs', 'is_mpi','EQUALS', 0); -% fp.where('Runs', 'fiber_length','EQUALS', 10); -fp.where('Runs', 'wavelength','EQUALS', 1310); -fp.where('Runs', 'db_mode','EQUALS', 1); -fp.where('Runs', 'rop_attenuation','EQUALS', 0); -fp.where('Runs', 'pam_level','EQUALS', 4); -% fp.where('Runs', 'bitrate','EQUALS', 360e9); -% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); -[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); - -% === Initialize DataStorage === -wh = DataStorage(dsp_options.parameters); -wh.addStorage("ffe_package"); -wh.addStorage("mlse_package"); -wh.addStorage("vnle_package"); -wh.addStorage("dbtgt_package"); -wh.addStorage("dbenc_package"); - -% === RUN IT === - -[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); -% wh.getStoValue('ffe_package',0.005); -% wh.getStoValue('mlse_package',0.005); - -[dataTable,~] = db.queryDB(fp, [db.getTableFieldNames('Runs');db.getTableFieldNames('Results');db.getTableFieldNames('Equalizer')]); - -dataTable = cleanUpTable(dataTable); - -% === Look at it === -y_var = 'BER_precoded'; -x_var = 'bitrate'; -fixedVars = {'equalizer_structure', x_var}; - -[dataTableClean, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var); - -% --- Group and aggregate --- -dataTableGrpd_mean = groupIt(fixedVars, dataTableClean, @mean); -dataTableGrpd_min = groupIt(fixedVars, dataTableClean, @min); -dataTableGrpd_max = groupIt(fixedVars, dataTableClean, @max); - -% Choose a color map -cols = linspecer(numel(unique(dataTableGrpd_mean.equalizer_structure))); - -figure; -hold on; - -% Get unique equalizer structures for grouping -unique_eq = unique(dataTableGrpd_mean.equalizer_structure); - -for i = 1:numel(unique_eq) - eq_val = unique_eq(i); - - % Filter grouped data for this equalizer structure - filt = dataTableGrpd_mean.equalizer_structure == eq_val; - - x = dataTableGrpd_mean.(x_var)(filt); - y_mean = dataTableGrpd_mean.(y_var)(filt); - y_min = dataTableGrpd_min.(y_var)(filt); - y_max = dataTableGrpd_max.(y_var)(filt); - - % Bounds for boundedline (distance from mean) - y_lower = y_mean - y_min; - y_upper = y_max - y_mean; - y_bounds = [y_lower, y_upper]; - - % --- Bounded line (mean ± min/max) --- - if exist('boundedline', 'file') - [hl, hp] = boundedline(x, y_mean, y_bounds, ... - 'alpha', 'transparency', 0.1, ... - 'cmap', cols(i,:), ... - 'nan', 'fill', ... - 'orientation', 'vert'); - set(hl, 'LineWidth', 1.2, 'DisplayName', sprintf('Eq %s', eq_val)); - set(hp, 'HandleVisibility', 'off'); - else - % If boundedline is not available, use errorbar - errorbar(x, y_mean, y_lower, y_upper, ... - 'o-', 'Color', cols(i,:), 'LineWidth', 1.2, ... - 'DisplayName', sprintf('Eq %d', eq_val),'HandleVisibility', 'off'); - end - - % --- Normal line (mean only) --- - plot(x, y_mean, '-', 'Color', cols(i,:), 'LineWidth', 1.5, ... - 'DisplayName', sprintf('Mean Eq %s', eq_val),'HandleVisibility', 'off'); - - % --- Scatter plot for individual points (from original data) --- - % Filter original data for this group - orig_filt = dataTableClean.equalizer_structure == eq_val; - x_scatter = dataTableClean.(x_var)(orig_filt); - y_scatter = dataTableClean.(y_var)(orig_filt); - - scatter(x_scatter, y_scatter, 10,cols(i,:), 'filled', ... - 'MarkerFaceAlpha', 0.5, 'DisplayName', sprintf('Scatter Eq %s', eq_val),'HandleVisibility', 'off'); -end - -yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); -set(gca, 'YScale', 'log'); % BER is usually plotted log-scale -xlabel(x_var, 'Interpreter', 'none'); -ylabel(y_var, 'Interpreter', 'none'); -legend('show', 'Location', 'best'); -grid on; -title(sprintf('%s vs. %s', y_var, x_var), 'Interpreter', 'none'); -hold off; \ No newline at end of file diff --git a/projects/Job_Processing/run_offline_dsp_script.m b/projects/Job_Processing/run_offline_dsp_script.m deleted file mode 100644 index a75b3bc..0000000 --- a/projects/Job_Processing/run_offline_dsp_script.m +++ /dev/null @@ -1,116 +0,0 @@ -dsp_options.append_to_db = false; -dsp_options.max_occurences = 1; -dsp_options.mode = "load_run_id"; -experiment = "highspeed_2024"; - -if dsp_options.mode == "load_run_id" - - dsp_options.load_file_path = []; - if experiment == 'highspeed_2024' - - dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db'; - dsp_options.storage_path = 'Z:\2024\sioe_labor\'; - db = DBHandler("pathToDB", [dsp_options.database_path], "type", "sqlite"); - - elseif 'mpi_ecoc_2025' - - dsp_options.database_path = []; %sqlite im netzwerk - dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\'; - db = DBHandler("pathToDB", [dsp_options.database_path], "type", "mysql"); - - end - - -elseif dsp_options.mode == "load_files" - - dsp_options.load_file_path.tx_bits_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091513_PAM_4_R_112_bits.mat"'; - dsp_options.load_file_path.tx_symbols_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091513_PAM_4_R_112_symbols.mat"'; - dsp_options.load_file_path.rx_raw_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091525_PAM_4_R_112_rec01_rx_signal_raw.mat"'; - -elseif dsp_options.mode == "simulate" - - error('Not yet implemented') - -end - - -%% === Define Database Filters === - -if experiment == 'highspeed_2024' - - fp = QueryFilter(); - db = db.getDistinctValues; - %fp.where('Runs', 'run_id','EQUALS', 3); - fp.where('Runs', 'pam_level','EQUALS', 4); - allrates = string(num2str(sort(db.distinctValues.Runs.bitrate.*1e-9))); - fp.where('Runs', 'bitrate','EQUALS',390e9); - alllengths = string(num2str(sort(db.distinctValues.Runs.fiber_length))); - fp.where('Runs', 'fiber_length','EQUALS', 1); - 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', 1310); - fp.where('Runs', 'db_mode','EQUALS', 0); - % fp.where('Runs', 'rop_attenuation','EQUALS', 0); - fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); - -elseif experiment == 'mpi_ecoc_2025' - - fp = QueryFilter(); - %fp.where('Runs', 'run_id','EQUALS', 3); - fp.where('Runs', 'pam_level','EQUALS', 4); - fp.where('Runs', 'bitrate','EQUALS',224e9); - fp.where('Runs', 'is_mpi','EQUALS', 1); - 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', 0); - % fp.where('Runs', 'rop_attenuation','EQUALS', 0); - fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); - % fp.where('Runs', 'loop_id','EQUALS', 14); - -end - - -% === Queue from DB === -[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); - -% === Adapt or filter Matlab Table === -% [~, unique_indices] = unique(dataTable.sir, 'first'); -% dataTable = dataTable(unique_indices, :); - - -% === Set LOOPS & Initialize DataStorage === -dsp_options.parameters = struct(); -% dsp_options.parameters.adaption = [3]; -% dsp_options.parameters.mu_tr = 0.999;%[0,logspace(-4,0,10)]; -% dsp_options.parameters.mu_dd = [logspace(-0.004,0,20)]; -% dsp_options.parameters.use_dd_mode = [1]; - -wh = DataStorage(dsp_options.parameters); -wh.addStorage("ffe_package"); -wh.addStorage("mlse_package"); -wh.addStorage("vnle_package"); -wh.addStorage("dbtgt_package"); -wh.addStorage("dbenc_package"); - - -% === RUN IT === -% run this function: dsp_runid(run_id, options) -[results,wh] = submitJobs(dataTable.run_id, dsp_options, "serial", 'wh', wh, 'waitbar', false); - - -%% -BERs = cellfun(@(c) c.ffe_package{1,1}.metrics.BER, results); -figure -hold on -plot(dsp_options.parameters.mu_dd,BERs,'Marker','+') -yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); -set(gca, 'YScale', 'log'); % BER is usually plotted log-scale -xlabel('$\mu$ DD', 'Interpreter', 'latex'); -ylabel('BER', 'Interpreter', 'latex'); -legend('show', 'Location', 'best'); -grid on; -hold off; \ No newline at end of file diff --git a/projects/Lab_2024/offline_dsp_analysis/load_n_plot_transfer_characteristic.m b/projects/Lab_2024/offline_dsp_analysis/load_n_plot_transfer_characteristic.m index 17d04f5..533892a 100644 --- a/projects/Lab_2024/offline_dsp_analysis/load_n_plot_transfer_characteristic.m +++ b/projects/Lab_2024/offline_dsp_analysis/load_n_plot_transfer_characteristic.m @@ -6,3 +6,11 @@ precomp_filename = "lab_high_speed"; freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',92e9); freqresp.load('loadPath',precomp_path,'fileName',precomp_filename); freqresp.plot(); + + +freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',256e9); +precomp_path = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\HighSpeedExperiment_2024\Auswertung_JLT"; +precomp_fn = "precomp_simulated.mat"; + +freqresp.load('loadPath',precomp_path,'fileName',precomp_fn); +freqresp.plot(); \ No newline at end of file From f4a22d23a2dd67ecf000ccce4c28437013a277bd Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Mon, 11 Aug 2025 16:31:09 +0200 Subject: [PATCH 08/30] SD stuff working for PAM6 and MLSE is also calibrated --- Classes/01_transmit/PAMmapper.m | 23 +- Classes/04_DSP/Sequence Detection/MLSE.m | 185 ++++++++------- Functions/Metrics/calc_gmi_bitwise.m | 206 ++++++++++------ Functions/Metrics/calc_ngmi.m | 258 +++++++++++++-------- projects/IMDD_base_system/simulation_bwl.m | 170 ++++++-------- 5 files changed, 490 insertions(+), 352 deletions(-) diff --git a/Classes/01_transmit/PAMmapper.m b/Classes/01_transmit/PAMmapper.m index cf4df17..a44d8cd 100644 --- a/Classes/01_transmit/PAMmapper.m +++ b/Classes/01_transmit/PAMmapper.m @@ -29,7 +29,7 @@ classdef PAMmapper obj.levels = obj.get_levels(); - obj.scaling = rms(obj.get_levels()); + obj.scaling = obj.get_scaling;%rms(obj.get_levels()); obj.eth_style = options.eth_style; @@ -283,6 +283,21 @@ classdef PAMmapper end end + function scaling = get_scaling(obj) + switch obj.M + case 2 + scaling = 1; + case 4 + scaling = sqrt(5); + case 6 + scaling = sqrt(10); + case 8 + scaling = sqrt(21); + case 16 + scaling = 1; + end + end + function [data_out] = demap_(obj,data_in) data_in= data_in'; @@ -450,7 +465,7 @@ classdef PAMmapper function [Signal_out] = quantize(obj,Signal_in) constellation = obj.get_levels(); - constellation = constellation ./ rms(constellation); + constellation = constellation ./ obj.scaling; issignalclass = 0; if isa(Signal_in,'Signal') @@ -481,7 +496,9 @@ classdef PAMmapper end function bitmap = showBitMapping(obj) - bitmap = obj.demap([obj.levels ./ obj.scaling]'); + + bitmap = obj.demap((obj.levels ./ obj.scaling)'); + end end diff --git a/Classes/04_DSP/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index e9d99dc..3883877 100644 --- a/Classes/04_DSP/Sequence Detection/MLSE.m +++ b/Classes/04_DSP/Sequence Detection/MLSE.m @@ -83,11 +83,6 @@ classdef MLSE < handle % impulse respnse to remove from signal obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000 - % % make the combined impulse-response have net gain = 1 - % h = obj.DIR(:); - % h = h / sum(h); - % obj.DIR = h.'; - % Normalize the Trellis states to =1 RMS obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states); @@ -122,21 +117,54 @@ classdef MLSE < handle count_row = 1; end - % first: RMS normalization of input data (rms==1) - data_in = data_in ./ rms(data_in); - data_in = data_in - mean(data_in); + % Was used earlier from Tom Wettlin etc. Sufficient for Viterbi + % but the BCJR is sensitive to the scaling, so I use another + % apporach + % % first: RMS normalization of input data (rms==1) + % data_in = data_in ./ rms(data_in); + % + % % then, match amplitude levels of input signal to those of the calculated ideal symbols + % % i.e. match the rms values of data_in to noise_free_received (rms=1.xx) + % if isreal(data_in) + % if obj.M == round(obj.M) + % data_in = data_in * rms(noise_free_received(noise_free_received ~= inf),'all','omitnan'); + % end + % end - % then, match amplitude levels of input signal to those of the calculated ideal symbols - % i.e. match the rms values of data_in to noise_free_received (rms=1.xx) - if isreal(data_in) - if obj.M == round(obj.M) - data_in = data_in * rms(noise_free_received(noise_free_received ~= inf),'all','omitnan'); - end + % quick and dirty alignment with xcorr + y = data_in; + x = data_ref; + h = flip(obj.DIR(:)).'; + y_ideal = conv(x, h, "same"); + [c,lags] = xcorr(y, y_ideal, 64); % small max lag is enough + [~,ix] = max(abs(c)); + lag = lags(ix); + y_ideal = circshift(y_ideal, lag); + + % center, skip transients, rm mean + skip = max(100, numel(obj.DIR)+50); + y_prep = y(1+skip:end-skip) - mean(y(1+skip:end-skip)); + y_ideal_prep = y_ideal(1+skip:end-skip) - mean(y_ideal(1+skip:end-skip)); + + % one-tap LS/MMSE gain and optional bias + g = (y_ideal_prep' * y_prep) / (y_ideal_prep' * y_ideal_prep); % complex allowed + b = mean(data_in) - g*mean(y_ideal); % intercept (if you keep means) + + dp = dot(y_ideal_prep, y_prep - g*y_ideal_prep); %shall be close to zero + + sigma2 = mean(abs(y_prep - g*y_ideal_prep).^2); + inv2s2 = 1/(2*sigma2); + + debug = 0; + if debug + figure(100); hold on + plot(1:length(y),y,'DisplayName','Y: Received Signal','LineStyle','none','Marker','.','MarkerSize',1); + plot(1:length(y_ideal),y_ideal,'DisplayName','Y ideal: x * DIR','LineStyle','none','Marker','.','MarkerSize',1); + yline(noise_free_received(:),'DisplayName','All Transition States','HandleVisibility','off','Color','red'); + yline(g*noise_free_received(:)+ b,'DisplayName','Scaled Transition States','HandleVisibility','off'); end - y_clean = conv( data_ref, flip(obj.DIR), "same" ); - sigma2 = var( data_in - y_clean ); - inv2s2 = 1/(2*sigma2); + noise_free_received = (g*noise_free_received + b); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% FORWARD PASS (VITERBI -Alpha's) %%%%% @@ -199,50 +227,47 @@ classdef MLSE < handle end - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%% FORWARD PASS PAM 2,4,8 (Combine Alpha and Beta to yield LLP's) %%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% FORWARD PASS PAM 2,4,8 (Combine Alpha and Beta to yield LLP's) %%%%% - %calc the log probabilities (llp's) + %calc the log probabilities (llp's) - for k = 1:length(data_in) + for k = 1:length(data_in) - if k == 1 + if k == 1 - alpha_ = repmat(alpha(:,k)',[nStates,1])'; - beta_ = beta(:,k); + alpha_ = repmat(alpha(:,k)',[nStates,1])'; + beta_ = beta(:,k); - LLP(:,k) = max(alpha_ + beta_,[],2); + LLP(:,k) = max(alpha_ + beta_,[],2); - else + else - alpha_ = repmat(alpha(:,k-1)',[nStates,1])'; - gamma_ = bm_fw(:,:,k)'; - beta_ = beta(:,k); - - LLP(:,k) = max(alpha_ + gamma_,[],1) + beta_'; - end + alpha_ = repmat(alpha(:,k-1)',[nStates,1])'; + gamma_ = bm_fw(:,:,k)'; + beta_ = beta(:,k); + LLP(:,k) = max(alpha_ + gamma_,[],1) + beta_'; end - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%% FORWARD PASS PAM2,4,8 %%%%% + end - 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) + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%% FORWARD PASS PAM2,4,8 %%%%% - % 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 + 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) - % figure - % hold on; - % for i = 1:obj.M - % scatter(1:length(expLLP),expLLP(i,:),1,'.'); - % end - % scatter(1:length(expLLP),max(expLLP(:,:)),3,'.'); + % 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 @@ -250,30 +275,22 @@ classdef MLSE < handle % all possible transitions (for now 36, including the "edges" % of the QAM 32 constellation) - pam6transitions = combvec(obj.trellis_states,obj.trellis_states)'; % pam6transitions = + states = PAMmapper(6,0,"eth_style",0).levels; + pam6transitions = combvec(states,states)'; % pam6transitions = % [-5 -5; % -3 -5; % -1 -5; ... pam6bits = PAMmapper(6,0,"eth_style",0).demap(reshape(pam6transitions',[],1)./sqrt(10)); - pam6bits = reshape(pam6bits',5,[])'; - %pam6bits = - % 0 0 0 1 0 - % 0 0 0 1 0 - % 0 0 0 1 1 - % 1 0 0 1 1 - % 1 0 0 1 0 - % 1 0 0 1 0 - % 0 0 1 1 0 - % .... + pam6bits = reshape(pam6bits',5,[])'; - [ok1, idx_bit_0] = ismember(pam6transitions(:,1), obj.trellis_states); - [ok2, idx2] = ismember(pam6transitions(:,2), obj.trellis_states); + [ok1, idx_sym_1] = ismember(pam6transitions(:,1), states); + [ok2, idx_sym_2] = ismember(pam6transitions(:,2), states); assert(all(ok1)&all(ok2), 'Some transition amplitude not found in trellis_states') - pam6ind = [idx_bit_0, idx2]; + pam6ind = [idx_sym_1, idx_sym_2]; - tx_bits_pam6_reshaped = reshape(tx_bits,5,[])'; + tx_bits_pam6_reshaped = reshape(tx_bits,5,[])'; % N x 5 numPairs = floor(size(LLP,2)/2); LLR_exact = zeros(numPairs,5); @@ -289,33 +306,33 @@ classdef MLSE < handle prob2 = state_prob(:,symbol2); % 36 joint‐metrics M = log P(i)*P(j) = L1(i)+L2(j) - Mij = LLP1(pam6ind(:,1)) + LLP2(pam6ind(:,2)); - pij = prob1(pam6ind(:,1)) .* prob2(pam6ind(:,2)); + Mij = LLP1(pam6ind(:,1)) + LLP2(pam6ind(:,2)); + pij = prob1(pam6ind(:,1)) .* prob2(pam6ind(:,2)); % now for each of the 5 bits do exact-LLR or max-log for b = 1:num_bits - idx_bit_0 = pam6bits(:,b)==1; + idx_sym_1 = pam6bits(:,b)==1; idx_bit_1 = pam6bits(:,b)==0; %--- exact LLR from probabilities - P1 = sum(pij(idx_bit_0)); + P1 = sum(pij(idx_sym_1)); P0 = sum(pij(idx_bit_1)); LLR_exact(k,b) = log(P1./P0); %--- max-log: - LLR_maxlogmap(k,b) = max( Mij(idx_bit_0) ) - max( Mij(idx_bit_1) ); % N x num_bits + LLR_maxlogmap(k,b) = max( Mij(idx_sym_1) ) - max( Mij(idx_bit_1) ); % N x num_bits end end - MI = zeros(1, num_bits); + MI = zeros(1, num_bits); for k = 1:num_bits idx_bit_1 = (tx_bits_pam6_reshaped(:,k) == 0); %wo sind die 1en - idx_bit_0 = (tx_bits_pam6_reshaped(:,k) == 1); %wo sind die 0en + 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_bit_0,k); + 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 @@ -324,7 +341,7 @@ classdef MLSE < handle end GMI = sum(MI); % Total mutual information per symbol - GMI = GMI/2; + GMI = GMI/2; else @@ -340,14 +357,14 @@ classdef MLSE < handle 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_sym_1 = bit_mapping(:,bit_idx) == 0; idx_bit_1 = bit_mapping(:,bit_idx) == 1; % Sum over log-probabilities (Max-Log approximation: using max instead of sum) - LLR_maxlogmap(:,bit_idx) = max(LLP(idx_bit_1,:), [], 1) - max(LLP(idx_bit_0,:), [], 1); + LLR_maxlogmap(:,bit_idx) = max(LLP(idx_bit_1,:), [], 1) - max(LLP(idx_sym_1,:), [], 1); % Sum probabilities over states for which the bit is 1 and 0, respectively. - P0 = sum(state_prob(idx_bit_0, :),1); + P0 = sum(state_prob(idx_sym_1, :),1); P1 = sum(state_prob(idx_bit_1, :),1); LLR_exact(:,bit_idx) = log(P1./P0); % N x num_bits @@ -356,16 +373,16 @@ classdef MLSE < handle %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% CALC NGMI %%%%% - MI = zeros(1, num_bits); + MI = zeros(1, num_bits); LLR_exact = LLR_exact; for k = 1:num_bits idx_bit_1 = (tx_bits(:,k) == 0); %wo sind die 1en - idx_bit_0 = (tx_bits(:,k) == 1); %wo sind die 0en + idx_sym_1 = (tx_bits(:,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_bit_0,k); + 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 @@ -379,7 +396,7 @@ classdef MLSE < handle VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS); - debug = 1; + if debug %%% DEBUG PLOT LIKELIHOOD RATIOS %%% figure(115);clf @@ -389,7 +406,7 @@ classdef MLSE < handle 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; @@ -403,7 +420,7 @@ classdef MLSE < handle if debug tx_bits = reshape(tx_bits',[],1); - + fprintf('\n') disp('Start DEBUG MLSE:') % DECIDE based on Viterbi traceback VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS); @@ -443,7 +460,7 @@ classdef MLSE < handle [~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); fprintf('FW BER: %.2e \n',ber_fw); disp('Stop DEBUG MLSE:') - disp('') + fprintf('\n') end end diff --git a/Functions/Metrics/calc_gmi_bitwise.m b/Functions/Metrics/calc_gmi_bitwise.m index 1ce8847..0b9f0b4 100644 --- a/Functions/Metrics/calc_gmi_bitwise.m +++ b/Functions/Metrics/calc_gmi_bitwise.m @@ -1,94 +1,158 @@ function [GMI,NGMI] = calc_gmi_bitwise(test_signal,reference_signal,options) % https://cioffi-group.stanford.edu/doc/book/AppendixG.pdf +% Supports PAM2/4/8 (per-symbol) and PAM6 (pairwise: 5 bits per 2 symbols) arguments(Input) - test_signal; - reference_signal; - options.skip_front = 0; - options.skip_end = 0; - options.returnErrorLocation = 0; + test_signal + reference_signal + options.skip_front (1,1) double = 0 + options.skip_end (1,1) double = 0 + options.returnErrorLocation (1,1) double = 0 %#ok end -options.skip_end = abs(options.skip_end); +options.skip_end = abs(options.skip_end); options.skip_front = abs(options.skip_front); +assert((options.skip_end+options.skip_front) < numel(test_signal), ... + "You can not skip more samples than overall length."); -assert((options.skip_end+options.skip_front) constant; included for shaped inputs) + logP = log(priors); + + % Precompute masks for bit=0 / bit=1 in the 6x6 grid + mask1 = false(6,6,5); + mask0 = false(6,6,5); + for b = 1:5 + tmp = false(6,6); + tmp(:) = pairBits(:,b)==1; + mask1(:,:,b) = tmp; + tmp = false(6,6); + tmp(:) = pairBits(:,b)==0; + mask0(:,:,b) = tmp; + end + + % Exact LLRs via log-sum-exp over the 6x6 grid + LLR = zeros(numPairs,5); + cst = -0.5*log(2*pi*sigma2); % cancels in LLR but harmless + for k = 1:numPairs + li = cst - ((y1(k) - levels).^2)/(2*sigma2) + logP; % 1x6 + lj = cst - ((y2(k) - levels).^2)/(2*sigma2) + logP; % 1x6 + logW = li(:) + lj(:).'; % 6x6 (adds logP twice) + + for b = 1:5 + L1 = logsumexp(logW(mask1(:,:,b))); + L0 = logsumexp(logW(mask0(:,:,b))); + LLR(k,b) = L1 - L0; % natural-log LLR + end + end + + % Bit-wise MI using consistency relation + MI_bits = zeros(1,5); + for b = 1:5 + idx0 = (tx_bits_pair(:,b)==0); + idx1 = ~idx0; + r0 = LLR(idx0,b); + r1 = LLR(idx1,b); + I0 = mean( log2(1 + exp( r0)) ); % natural LLR inside exp() + I1 = mean( log2(1 + exp(-r1)) ); + MI_bits(b) = 1 - 0.5*(I0 + I1); + end + + % Per-symbol outputs (5 bits per 2 symbols) + GMI = sum(MI_bits)/2; + NGMI = GMI / 2.5; + +% ----- Generic PAM2/4/8…: per-symbol, Gray bits directly on symbols ----- +else + nBits = log2(M); + levels = PAMmapper(M,0).levels' / PAMmapper(M,0).scaling; % match PAMmapper ordering + grayBits= PAMmapper(M,0).showBitMapping; % M x nBits + + % Allocate + LLR = zeros(N, nBits); + + for n = 1:N + y = test_signal(n); + ll_table = -((y - levels).^2)/(2*sigma2) + log(priors); % 1xM (log-domain) + for b = 1:nBits + idx0 = grayBits(:,b)==0; + idx1 = ~idx0; + L0 = logsumexp(ll_table(idx0)); + L1 = logsumexp(ll_table(idx1)); + LLR(n,b) = L1 - L0; % natural-log LLR + end + end + + tx_bits = PAMmapper(M,0,"eth_style",0).demap(reference_signal); + + % Bit-wise MI + MI_bits = zeros(1,nBits); for b = 1:nBits - idx0 = grayBits(:,b)==0; - idx1 = grayBits(:,b)==1; - L0 = logsumexp(ll_table(idx0)); - L1 = logsumexp(ll_table(idx1)); - LLR(n,b) = L1 - L0; + r0 = LLR(tx_bits(:,b)==0,b); + r1 = LLR(tx_bits(:,b)==1,b); + I0 = mean( log2(1 + exp( r0)) ); + I1 = mean( log2(1 + exp(-r1)) ); + MI_bits(b) = 1 - 0.5*(I0 + I1); end + + GMI = sum(MI_bits); % bits / symbol + NGMI = GMI / nBits; end -tx_bits = PAMmapper(M,0,"eth_style",0).demap(reference_signal); - -% Compute GMI (bit‐wise MI averaged over all symbols) -MI_bits = zeros(1,nBits); -for b = 1:nBits - r0 = LLR(tx_bits(:,b)==0,b); - r1 = LLR(tx_bits(:,b)==1,b); - I0 = mean( log2(1 + exp(r0)) ); - I1 = mean( log2(1 + exp( -r1)) ); - MI_bits(b) = 1 - 0.5*(I0 + I1); +% ===== Helpers ===== +function s = logsumexp(a) + if isempty(a), s = -inf; return; end + m = max(a(:)); + s = m + log(sum(exp(a(:) - m))); end -GMI = sum(MI_bits); % in bits per symbol -NGMI = GMI / nBits; % normalized per bit - -% 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))); - end - - - function [data_,reference_]=trimseq(data,reference,skipstart,skip_end) - - data_ = data(skipstart+1:end-skip_end,:); - - delta_bits = length(reference) - length(data); - - skip_end = delta_bits + skip_end; - - reference_ = reference(skipstart+1:end-skip_end,:); - - end - -end \ No newline at end of file +function [data_,reference_] = trimseq(data,reference,skipstart,skip_end) + data_ = data(skipstart+1:end-skip_end,:); + delta = numel(reference) - numel(data_); + reference_ = reference(skipstart+1:end-(skip_end+delta),:); +end +end diff --git a/Functions/Metrics/calc_ngmi.m b/Functions/Metrics/calc_ngmi.m index afbbf54..5d7363a 100644 --- a/Functions/Metrics/calc_ngmi.m +++ b/Functions/Metrics/calc_ngmi.m @@ -1,125 +1,181 @@ function [GMI,NGMI] = calc_ngmi(test_signal,reference_signal,options) -% Silas implementation of (N)GMI calculation according to: J. Cho, L. Schmalen, und P. J. Winzer, -% „Normalized Generalized Mutual Information as a Forward Error Correction Threshold for Probabilistically Shaped QAM“, -% in 2017 European Conference on Optical Communication (ECOC), Sep. 2017, doi: 10.1109/ECOC.2017.8345872. - -% This implementation assumes the same normal distributed noise for each -% channel (sigma2 is calculated once for the whole signal) +% (N)GMI for AWGN, supports PAM2/4/8 (per-symbol) and PAM6 (pair-based, 5 bits / 2 symbols) arguments(Input) - test_signal; - reference_signal; - options.skip_front = 0; - options.skip_end = 0; - options.returnErrorLocation = 0; + test_signal + reference_signal + options.skip_front (1,1) double = 0 + options.skip_end (1,1) double = 0 + options.returnErrorLocation (1,1) double = 0 %#ok end -options.skip_end = abs(options.skip_end); +options.skip_end = abs(options.skip_end); options.skip_front = abs(options.skip_front); +assert((options.skip_end+options.skip_front) < numel(test_signal), ... + "You can not skip more samples than the overall length."); -assert((options.skip_end+options.skip_front) bit label) and (symbol -> index) + [ok1, idx_s1] = ismember(trans_pairs(:,1), symbols); + [ok2, idx_s2] = ismember(trans_pairs(:,2), symbols); + assert(all(ok1)&all(ok2), 'Transition level not found in symbols.'); + % For fast masking: linear indices of each (i,j) pair in an MxM grid + lin_idx_pairs = sub2ind([M,M], idx_s1, idx_s2); % (M^2) x 1 - error_vector = (test_signal-reference_signal); - sigma2 = var(error_vector); %noise variance + % Build pairwise prior P_pair = P_X(i)*P_X(j) on the MxM grid + P_pair = P_X(:) * P_X(:).'; % MxM + logP_pair = log(P_pair); % for numerical stability - %%% Separate Classes - constellation = unique(reference_signal); - received_sd = NaN(numel(constellation),length(reference_signal)); - lvlcol = cbrewer2('Set1',numel(constellation)); - for lvl = 1:numel(constellation) - %Separate the equalized signal into the - %respective levels based on the actually - %transmitted level! - received_sd(lvl,reference_signal==constellation(lvl)) = test_signal(reference_signal==constellation(lvl)); - end + % Precompute constant term for log-likelihood + logc = -0.5*log(2*pi*sigma2); - N = length(test_signal); % Number of received samples - - M = length(constellation); %P - m = log2(M); %bits per symbol - - entries = sum(~isnan(received_sd),2)'; - P_X = ones(1,M)/M;%entries./N; - - % Parameters - symbols = constellation'; % PAM-4 symbols - - gray_bits = PAMmapper(M,0).demap_(constellation); % Gray coding (bits per symbol) - - % Conditional probability function for AWGN - q_Y_given_X = @(y, x) (1 / sqrt(2 * pi * sigma2)) * exp(-(y - x).^2 / (2 * sigma2)); - - % Entropy term - H_X = -sum(P_X .* log2(P_X)); % Entropy of input distribution - - tx_bits = PAMmapper(M,0,"eth_style",0).demap(reference_signal); - - % GMI computation - bit_llr_sum = 0; - for k = 1:N %loop over signal - y_k = test_signal(k); % Current received sample - % [~, closest_symbol_idx] = min(abs(symbols - y_k)); % Closest symbol index - - for i = 1:m %loop over bit position - % Extract i-th bit for each symbol - bit_mask = gray_bits(:, i); % Binary column for i-th bit of all symbols - - % matching_symbols = symbols(bit_mask == gray_bits(closest_symbol_idx, i)); %old, based on decision that mihht be wrong - matching_symbols = symbols(bit_mask == tx_bits(k,i)); %new, based on tx bits - - % Numerator: Sum over x in x_{b_{k, i}} - numerator = sum(q_Y_given_X(y_k, matching_symbols) .* P_X(ismember(symbols, matching_symbols))); - - % Denominator: Sum over all x - denominator = sum(q_Y_given_X(y_k, symbols) .* P_X); - - % Logarithmic contribution - bit_llr_sum = bit_llr_sum + log2(numerator / denominator); - end - end - - % Normalize the noise impact term by N - bit_llr_sum = bit_llr_sum / N; - - % GMI - GMI = H_X + bit_llr_sum; - NGMI = GMI / m; + % Prepare transmitted 5-bit labels per observed pair (to split llr0/llr1) + % Find each (x1(k),x2(k)) in trans_pairs: + [tf, row_in_table] = ismember([x1(:), x2(:)], trans_pairs, 'rows'); + assert(all(tf), 'A reference pair was not found in the transition table.'); + tx_bits_pair = pam6bits(row_in_table, :); % numPairs x 5 + % Precompute masks for bit=0 / bit=1 on the MxM grid for each bit position + mask1 = false(M,M,5); + mask0 = false(M,M,5); + for b = 1:5 + tmp = false(M,M); + tmp(lin_idx_pairs) = pam6bits(:,b) == 1; + mask1(:,:,b) = tmp; + tmp = false(M,M); + tmp(lin_idx_pairs) = pam6bits(:,b) == 0; + mask0(:,:,b) = tmp; end - function [data_,reference_]=trimseq(data,reference,skipstart,skip_end) - - data_ = data(skipstart+1:end-skip_end,:); - - delta_bits = length(reference) - length(data); - - skip_end = delta_bits + skip_end; - - reference_ = reference(skipstart+1:end-skip_end,:); + % Loop pairs: compute exact LLRs via log-sum-exp over the MxM grid + LLR_exact = zeros(numPairs,5); + for k = 1:numPairs + % log-likelihood vectors along each axis + li = logc - ((y1(k) - symbols).^2) / (2*sigma2); % 1xM + lj = logc - ((y2(k) - symbols).^2) / (2*sigma2); % 1xM + % joint log-weights over all (i,j) + logW = (li(:) + lj(:).') + logP_pair; % MxM + for b = 1:5 + % log P(bit=1 | y) ∝ logsumexp(logW over mask1) + lnum = logsumexp(logW(mask1(:,:,b))); + % log P(bit=0 | y) ∝ logsumexp(logW over mask0) + lden = logsumexp(logW(mask0(:,:,b))); + LLR_exact(k,b) = lnum - lden; % natural-log LLR + end end -end \ No newline at end of file + % --- Bitwise MI via consistency relation + MI = zeros(1,5); + for b = 1:5 + llr_b = LLR_exact(:,b); + idx0 = (tx_bits_pair(:,b)==0); + idx1 = ~idx0; + I0 = mean( log2(1 + exp( llr_b(idx0))) ); % natural LLR inside exp() + I1 = mean( log2(1 + exp(-llr_b(idx1))) ); + MI(b) = 1 - 0.5*(I0 + I1); + end + + % GMI per symbol (5 bits per 2 symbols) + GMI = sum(MI)/2; + NGMI = GMI / 2.5; + +else + % ===== Generic PAM2/4/8 etc.: per-symbol bit labeling ===== + symbols = constellation; % 1xM + % Gray labels per symbol (your helper) + gray_bits = PAMmapper(M,0).demap_(symbols); % M x log2(M) + m = log2(M); + + % Likelihood (real AWGN) + q = @(y,x) (1/sqrt(2*pi*sigma2)) * exp(-(y - x).^2/(2*sigma2)); + + % Transmitted bits per sample + tx_bits = PAMmapper(M,0,"eth_style",0).demap(reference_signal); % N x m + + % Exact bit-LLRs and MI + LLR_exact = zeros(N,m); + for b = 1:m + mask1 = (gray_bits(:,b)==1); + mask0 = ~mask1; + for k = 1:N + yk = test_signal(k); + den = sum(q(yk, symbols) .* P_X); + num1 = sum(q(yk, symbols(mask1)) .* P_X(mask1)); + num0 = sum(q(yk, symbols(mask0)) .* P_X(mask0)); + % Use the ratio of posteriors P(bit=1|y)/P(bit=0|y) + LLR_exact(k,b) = log(num1) - log(num0); % natural log + end + end + + % Bitwise MI + MI = zeros(1,m); + for b = 1:m + llr_b = LLR_exact(:,b); + idx0 = (tx_bits(:,b)==0); + idx1 = ~idx0; + I0 = mean( log2(1 + exp( llr_b(idx0))) ); + I1 = mean( log2(1 + exp(-llr_b(idx1))) ); + MI(b) = 1 - 0.5*(I0 + I1); + end + + % Per-symbol GMI/NGMI + GMI = sum(MI); + NGMI = GMI / m; +end + +% ---------- helpers ---------- +function s = logsumexp(a) + if isempty(a), s = -inf; return; end + amax = max(a(:)); + s = amax + log(sum(exp(a(:) - amax))); +end + +function [data_,reference_] = trimseq(data,reference,skipstart,skip_end) + data_ = data(skipstart+1:end-skip_end,:); + delta = numel(reference) - numel(data_); + reference_ = reference(skipstart+1:end-(skip_end+delta),:); +end +end diff --git a/projects/IMDD_base_system/simulation_bwl.m b/projects/IMDD_base_system/simulation_bwl.m index f47b0aa..b040f1e 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 = 4; +M = 6; fsym = 112e9; apply_pulsef = 1; @@ -44,9 +44,9 @@ dfe_ = sum(dfe_order)>0; doub_mode = db_mode.no_db; -rop = [-8]; +rop = [-5]; bwl = [0.5:0.1:1.5]; -fsym = [128:16:170].*1e9; +fsym = [72:8:170].*1e9; ber_vnle = []; ber_mlse = []; ber_viterbi = []; @@ -56,7 +56,7 @@ gmi_vnle = []; gmi_mlse = []; gmi_mlse_db = []; -for r = 1:length(fsym) +parfor r = 1:length(fsym) Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha); @@ -74,104 +74,88 @@ for r = 1:length(fsym) "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',0); - - - %%%%% AWG - % El_sig = M8199A("kover",kover).process(Digi_sig); - El_sig = AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); - % El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); - % El_sig = El_sig.setPower(0,"dBm"); - + 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); + %%%%% Low-pass el. components %%%%%% - % f_nyquist = fsym/2; - % tx_bwl = tx_bw_nyquist.*f_nyquist; tx_bwl = 50e9; El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); - % El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); - + %%%%% Electrical Driver Amplifier %%%%%% - El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); El_sig = El_sig.normalize("mode","oneone"); - + %%%%% MODULATE E/O CONVERSION %%%%%% [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 %%%%%% - Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); + Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); %%%%%% PD Square Law %%%%%% - Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig); + 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 = rx_bw_nyquist.*f_nyquist; rx_bwl = 50e9; - Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(Rx_sig); + 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); - % Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); - % %%%%%% 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',0,'H_lpf',Lp_scpe).process(Rx_sig); + "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',1,'H_lpf',Lp_scpe).process(PD_sig); - Scpe_sig_resampled = Scpe_sig.resample("fs_out",2*fsym(r)); + 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_resampled.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 1); - % Scpe_cell = cell(1); - % Scpe_cell{1} = Scpe_sig_resampled; + [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0); + Rx_sig = Scpe_cell{1}; - Scpe_cell{1}.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); + 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); - mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); + 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); + mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); - if 0 - %Duobinary Targeting - db_ref_sequence = Duobinary().encode(Symbols); - db_ref_constellation = unique(db_ref_sequence.signal); - [eq_signal, eq_noise] = eq_.process(Scpe_cell{1},db_ref_sequence); - - mlse_.DIR = [1,1]; - [mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); - - mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(mlse_sig_sd); - 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),~] = 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",0).demap(mlse_sig_hd); - [bits_mlse,errors_mlse,ber_db(r),~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",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); + + mlse_.DIR = [1,1]; + [mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); + + mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(mlse_sig_sd); + 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),~] = 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",0).demap(mlse_sig_hd); + [bits_mlse,errors_mlse,ber_db(r),~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); end % FFE or VNLE 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); - [eq_signal_sd, eq_noise] = eq_.process(Scpe_cell{1}, Symbols); - [gmi_gomez(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100); - [gmi_vnle(r)] = calc_ngmi(eq_signal_sd,Symbols); - [gmi_bitwise(r)] = calc_gmi_bitwise(eq_signal_sd,Symbols); + [eq_signal_sd, eq_noise] = eq_.process(Rx_sig, Symbols); + + [gmi_gomez(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100); + [gmi_vnle(r)] = calc_ngmi(eq_signal_sd,Symbols); + [gmi_bitwise(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',118); - % Hard decision on VNLE output eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); @@ -181,32 +165,33 @@ for r = 1:length(fsym) if 1 - % Process through postfilter and MLSE - pf_ncoeffs = 1; - pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); - mlse_ = MLSE("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; - 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); - [~,~,ber_mlse(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('BER: %.2e \n',ber_mlse(r)); - fprintf('GMI MLSE: %.5f \n',gmi_mlse(r)); + % Process through postfilter and MLSE + pf_ncoeffs = 1; + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + mlse_ = MLSE("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; + 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); + [~,~,ber_mlse(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + fprintf('BER: %.2e \n',ber_mlse(r)); + fprintf('GMI MLSE: %.5f \n',gmi_mlse(r)); + + % 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)); - % 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)); end @@ -224,19 +209,19 @@ ylabel('alpha'); figure(15);hold on plot(fsym.*1e-9,gmi_gomez,'DisplayName','gomez','Marker','x','LineStyle','-','Color',cols(1+d,:)); plot(fsym.*1e-9,gmi_vnle,'DisplayName','vnle other','Marker','x','LineStyle','-','Color',cols(3+d,:)); -plot(fsym.*1e-9,gmi_bitwise,'DisplayName','bitwise','Marker','x','LineStyle','--','Color',cols(5+d,:)); +% plot(fsym.*1e-9,gmi_bitwise,'DisplayName','bitwise','Marker','x','LineStyle','--','Color',cols(5+d,:)); plot(fsym.*1e-9,gmi_mlse,'DisplayName','MLSE','Marker','*'); -ylim([0 2.6]); +plot(fsym.*1e-9,gmi_mlse_db,'DisplayName','DB Output','Marker','*'); +ylim([log2(M)-1 log2(M)]); xlabel('Baudrate in GBd'); ylabel('GMI'); - figure(13);hold on plot(fsym.*1e-9,ber_vnle,'DisplayName','VNLE','Marker','x','LineStyle','-','Color',cols(1+d,:)); plot(fsym.*1e-9,ber_mlse,'DisplayName','MLSE','Marker','x','LineStyle','-','Color',cols(3+d,:)); plot(fsym.*1e-9,ber_viterbi,'DisplayName','Viterbi','Marker','x','LineStyle','--','Color',cols(5+d,:)); -% plot(bwl,ber_db_diff_precoded,'DisplayName','MLSE db diff','Marker','.','MarkerSize',15,'LineStyle','-'); -% plot(bwl,ber_db,'DisplayName','MLSE db','Marker','.','MarkerSize',15,'LineStyle','-'); +plot(fsym.*1e-9,ber_db_diff_precoded,'DisplayName','MLSE db diff','Marker','.','MarkerSize',15,'LineStyle','-'); +plot(fsym.*1e-9,ber_db,'DisplayName','MLSE db','Marker','.','MarkerSize',15,'LineStyle','-'); xlabel('Baudrate in GBd'); ylabel('BER'); set(gca, 'yscale', 'log'); @@ -244,7 +229,6 @@ set(gca, 'yscale', 'log'); legend - % Auxiliary nested helper for numerically stable log-sum-exp function s = logsumexp(a) % LOGSUMEXP Compute log(sum(exp(a))) in a numerically stable way From 4099f6820f7a98899aedf6599e37d97c1be9ac45 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 17 Sep 2025 13:58:58 +0200 Subject: [PATCH 09/30] BCJR implementation WDM code added (Pol Cont., Opt MUX/DEMUX, Opt Atten, DP_Fiber) -> the codebase is not optimized to always work with dp signals! --- Classes/00_signals/Opticalsignal.m | 2 + Classes/00_signals/Signal.m | 130 ++-- Classes/02_etc/Amplifier.m | 2 +- Classes/02_optical/DP_Fiber.m | 240 ++++++++ Classes/02_optical/EML.m | 2 +- Classes/02_optical/Optical_Demultiplex.m | 122 ++++ Classes/02_optical/Optical_Multiplex.m | 171 ++++++ Classes/02_optical/Polarization_Controller.m | 92 +++ Classes/04_DSP/Coding/Duobinary.m | 1 + Classes/04_DSP/Sequence Detection/MLSE.m | 261 +++++--- Classes/04_DSP/TransmissionPerformance.m | 52 +- Classes/DataBaseHandler/DBHandler.m | 184 ++++-- Classes/Warehouse_class/classes/DataStorage.m | 5 +- .../Warehouse_class/classes/minimal_example.m | 40 ++ Datatypes/polarization_control_mode.m | 10 + Functions/EQ_structures/dsp_runid.m | 81 ++- Functions/EQ_structures/duobinary_target.m | 40 +- Functions/EQ_structures/ffe.m | 5 +- .../EQ_structures/vnle_postfilter_mlse.m | 42 +- Functions/EQ_visuals/show2Dconstellation.m | 85 +++ Functions/EQ_visuals/showLevelScatter.m | 10 +- .../loadAndSyncSignalDataFromDb.m | 4 +- Functions/Metrics/calc_snr.m | 1 - Functions/Metrics/count_error_bursts.m | 4 +- .../ECOC_2025/dsp_test/hyperparam_tuning.m | 18 +- .../plots_from_database/plot_mpi_trial.m | 45 +- .../theory/analytic_mpi_evaluation.m | 82 +++ .../ECOC_2025/theory/coherence_length_plot.m | 32 + .../Auswertung_JLT/ber_vs_rate.m | 301 ++++++++-- .../Auswertung_JLT/run_dsp_from_db.m | 437 +++++++++----- projects/IMDD_base_system/simulation_bwl.m | 374 +++++++++--- projects/WDM/WDM_model.m | 268 +++++++++ test/bcjr_pam.m | 555 ++++++++++++++++++ test/duobinary_emulation_minimal_example.m | 76 +-- test/minimal_example_bcjr.m | 171 ++++++ test/pam_6_differential_code_understand.m | 79 +++ test/pam_6_states_analysis.m | 212 +++++++ 37 files changed, 3643 insertions(+), 593 deletions(-) create mode 100644 Classes/02_optical/DP_Fiber.m create mode 100644 Classes/02_optical/Optical_Demultiplex.m create mode 100644 Classes/02_optical/Optical_Multiplex.m create mode 100644 Classes/02_optical/Polarization_Controller.m create mode 100644 Classes/Warehouse_class/classes/minimal_example.m create mode 100644 Datatypes/polarization_control_mode.m create mode 100644 Functions/EQ_visuals/show2Dconstellation.m create mode 100644 projects/ECOC_2025/theory/analytic_mpi_evaluation.m create mode 100644 projects/ECOC_2025/theory/coherence_length_plot.m create mode 100644 projects/WDM/WDM_model.m create mode 100644 test/bcjr_pam.m create mode 100644 test/minimal_example_bcjr.m create mode 100644 test/pam_6_differential_code_understand.m create mode 100644 test/pam_6_states_analysis.m diff --git a/Classes/00_signals/Opticalsignal.m b/Classes/00_signals/Opticalsignal.m index 25900a4..b39511e 100644 --- a/Classes/00_signals/Opticalsignal.m +++ b/Classes/00_signals/Opticalsignal.m @@ -5,6 +5,7 @@ classdef Opticalsignal < Signal properties nase lambda + polrot end @@ -18,6 +19,7 @@ classdef Opticalsignal < Signal options.logbook options.lambda options.nase + options.polrot end obj = obj@Signal(signal); diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index 9b0c3e5..c84ebf6 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -108,6 +108,7 @@ classdef Signal options.logbook options.nase options.lambda + options.polrot end fn = fieldnames(options); @@ -122,7 +123,7 @@ classdef Signal %convert to optical - o_sig = Opticalsignal(obj.signal,"fs",obj.fs,"lambda",options.lambda,"logbook",obj.logbook,"nase",options.nase); + o_sig = Opticalsignal(obj.signal,"fs",obj.fs,"lambda",options.lambda,"logbook",obj.logbook,"nase",options.nase,"polrot",options.polrot); elseif isa(obj,'Informationsignal') @@ -141,7 +142,7 @@ classdef Signal arguments obj - options.fignum = [] + options.fignum = randi(1000) options.displayname = []; options.timeframe = 0; options.clear = 0; @@ -187,7 +188,7 @@ classdef Signal ylabel('Amplitude'); end - + % Add legend if not already present if isempty(get(gca, 'Legend')) legend; @@ -350,21 +351,30 @@ classdef Signal options.normalizeTo0dB = 0; options.max_num_lines = []; % Leave empty or omit to disable line rotation options.fft_length = []; + % --- NEW options --- + options.useWavelengthAxis (1,1) logical = false % plot x-axis in wavelength + options.lambda0_nm (1,1) double = 1310 % center wavelength [nm] end if isempty(options.fft_length) options.fft_length = 2^(nextpow2(length(obj.signal))-9); end - + if options.normalizeToNyquist == 0 - [p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,obj.fs,"centered","power","mean"); - w = w.*1e-9; + [p_lin,f_Hz] = pwelch(obj.signal, hanning(options.fft_length), ... + options.fft_length/2, options.fft_length, ... + obj.fs, "centered", "power", "mean"); + f_GHz = f_Hz*1e-9; % keep frequency vector for frequency axis else - [p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,"centered","power","mean"); + [p_lin,f_rad] = pwelch(obj.signal, hanning(options.fft_length), ... + options.fft_length/2, options.fft_length, ... + "centered", "power", "mean"); + % In normalized mode, pwelch returns rad/sample centered on 0. + % We'll keep f_rad for the x-axis in that mode. end if options.normalizeTo0dB - p_lin = p_lin./ max(p_lin); + p_lin = p_lin ./ max(p_lin); p_dbm = 10*log10(p_lin); % normalized to 0 dB ylab = "Normalized PSD"; else @@ -372,36 +382,69 @@ classdef Signal ylab = "Power (dB/Hz)"; end + % --- If requested, build wavelength axis from frequency offset --- + if options.useWavelengthAxis && options.normalizeToNyquist == 0 + c = physconst('LightSpeed'); % [m/s] + lambda0_m = options.lambda0_nm*1e-9; % center wavelength [m] + f_c = c / lambda0_m; % carrier frequency [Hz] + + % exact mapping + f_abs = f_c + f_Hz; % absolute frequency [Hz] + lambda_m = c ./ f_abs; % wavelength [m] + lambda_nm = lambda_m * 1e9; % wavelength [nm] + + % assign axis + x_vec = lambda_nm(:); + x_label = "Wavelength [nm]"; + + % Sort to ensure axis is ascending + [x_vec, sortIdx] = sort(x_vec, 'ascend'); + p_dbm = p_dbm(sortIdx, :); + else + % Frequency or normalized axes + if options.normalizeToNyquist == 0 + x_vec = f_GHz; + x_label = "Frequency in GHz"; + else + x_vec = f_rad; % normalized frequency in rad/sample + x_label = "Normalized Frequency"; + end + end + figure(options.fignum); ax = gca; hold on - if isempty(options.color) - hLine = plot(w,p_dbm,'DisplayName',options.displayname,'LineWidth',1); - else - hLine = plot(w,p_dbm,'DisplayName',options.displayname,'LineWidth',1,'Color',options.color); + 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); + end end - % If user wants to limit the number of lines, check and remove old lines + % Limit number of lines if requested if ~isempty(options.max_num_lines) && options.max_num_lines > 0 allLines = findall(ax, 'Type', 'Line'); if length(allLines) > options.max_num_lines - % Sort lines by creation order. Usually, the oldest lines appear first in allLines. - % If needed, you can sort by UserData or other criteria. numToRemove = length(allLines) - options.max_num_lines; delete(allLines(1:numToRemove)); end end - if options.normalizeToNyquist == 0 - xlabel("Frequency in GHz"); - edgetick = 2^(nextpow2(obj.fs*1e-9)); - xticks(-edgetick:32:edgetick); - xlim([100*round(min(w)/100,1)-10, 100*round(max(w)/100,1)+10]) - xlim([-128 128]);%256GSa/s + % Axis labels and limits + xlabel(x_label); + + if options.useWavelengthAxis && options.normalizeToNyquist == 0 + xlim([min(x_vec) max(x_vec)]); else - xlabel("Normalized Frequency"); - xlim([-pi, pi]); + if options.normalizeToNyquist == 0 + % Keep your existing freq handling (you can fine-tune as needed) + % xlim([-128 128]); % example for 256 GSa/s if desired + xlim([min(x_vec) max(x_vec)]); + else + xlim([-pi, pi]); + end end ylabel(ylab); @@ -409,24 +452,23 @@ classdef Signal 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))-3, ceil(max(p_dbm))+3]); + ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]); end if options.normalizeTo0dB - % ylim([-40, 3]); - ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]); + ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]); else - ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]); + ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]); end yticks(-200:10:10); - grid on;% grid minor; + grid on; legend - % legend('Interpreter','none'); end + function move_it_spectrum(obj,options) arguments obj @@ -554,7 +596,7 @@ classdef Signal options.unit power_notation = power_notation.dBm end - pow = mean(abs(obj.signal).^2); + pow = sum(mean(abs(obj.signal).^2)); switch options.unit case power_notation.dBm @@ -669,9 +711,9 @@ classdef Signal options.fs_ref = 0; options.debug_plots = 0; end - - - + + + S = {}; inverted = -1; sequenceFound = 0; @@ -715,25 +757,25 @@ classdef Signal findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum,'SortStr','descend') end - + shifts = lags(pkpos); sequenceStarts = shifts; shifts = shifts(shifts>=0); if numel(shifts) > 0 - + %Cut occurences of ref signal from signal (only positive shifts) if all(sign(co(pkpos))==-1) inverted = 1; end - + for c = shifts sig = obj.delay(-c,'mode','samples'); sig.signal = sig.signal(1:length(b));% .* -inverted; S{end+1,1} = sig; end - + % %return/keep the sinal with the highest correlation (only within positive shifts) % [~,idx]=max(pks); % obj.signal = S{idx}.signal; @@ -741,7 +783,7 @@ classdef Signal % swap = S{1}; % S{1} = S{idx}; % S{idx} = swap; - + for c = 1:numel(shifts) S{c}.logbook = []; end @@ -770,10 +812,6 @@ classdef Signal end - - - - %% function obj = filter(obj,a,b) @@ -934,8 +972,8 @@ classdef Signal nn=histcounts(data_ind_y(n,:),1:histpoints+1); hist_data(:,n)=flip(nn.'); %without flip, the eye is upside down :-( end - - + + plot_data = 20*log10(hist_data); plot_data(plot_data==-Inf) = 0; @@ -970,7 +1008,7 @@ classdef Signal end xlabel('Time in ps') - + % add information @@ -1091,7 +1129,7 @@ classdef Signal x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12); xticklabels(x_tickstring); - grid off + grid off end diff --git a/Classes/02_etc/Amplifier.m b/Classes/02_etc/Amplifier.m index 31324e2..71db478 100644 --- a/Classes/02_etc/Amplifier.m +++ b/Classes/02_etc/Amplifier.m @@ -108,7 +108,7 @@ classdef Amplifier if obj.gain_mode == gain_mode.output_power %get linear gain for output power mode - pow_in = mean(abs(xin.^2)) ; % lin input power + pow_in = sum(mean(abs(xin.^2)),2) ; % lin input power pow_out = 10^(obj.amplification_db/10 - 3) ; % dBm to lin a_lin = sqrt(pow_out/pow_in) ; diff --git a/Classes/02_optical/DP_Fiber.m b/Classes/02_optical/DP_Fiber.m new file mode 100644 index 0000000..a5f3dce --- /dev/null +++ b/Classes/02_optical/DP_Fiber.m @@ -0,0 +1,240 @@ +classdef DP_Fiber + % Dual-Polarization fiber propagation (CNLSE / Manakov) — class version + % Runs full setup + propagation inside process_ (no external loop). + + properties (Access=public) + % ---- User options (public) ---- + L % [km] fiber length + dz % [m] step size target (kept for compatibility; adaptive dz uses SS_* below) + lambda % [nm] reference wavelength + rng % RNG seed + gamma % [1/W/m] nonlinear coefficient + + % Optional / advanced options (match legacy names where possible) + fa % [Hz] sampling frequency + X_alpha % [dB/100km] attenuation per 100 km (per-pol, used for X and Y) + X_beta % [1..4] dispersion coefficients vector for X (Y mirrors X) + D % [ps/(nm*km)] + Ds % [ps/(nm^2*km)] dispersion slope + Dpmd % [ps/sqrt(km)] PMD coefficient + beat_len % [m] beat length (for beta(1) if X_beta is zero) + corr_len % [m] correlation length (not directly used; kept for compatibility) + manakov % 1=Manakov (legacy behavior: manakov=eq-1) + SS_dphimax % [rad] max nonlinear phase per step (adaptive SSFM) + SS_dzmax % [m] max dz (adaptive SSFM) + SS_dzmin % [m] min dz (adaptive SSFM) + n_waveplates % number of PMD waveplates + + % ---- Internal state (persistent between calls) ---- + state % struct mirroring legacy 'state' + end + + methods (Access=public) + function obj = DP_Fiber(options) + % Constructor — copies fields from 'options' and sets defaults. + + arguments + options.L + options.dz + options.lambda + options.rng = 0 + options.gamma + + % optional but recommended + options.fa + + % legacy-compatible optional params + options.X_alpha = 4.605170185988092e-05 % dB/100km + options.X_beta = [0,0,-2.16826193914149e-26,3.56839456298263e-41] + options.D = 17 % ps/(nm*km) + options.Ds = 0.06 % ps/(nm^2*km) + options.Dpmd = 3 % ps/sqrt(km) + options.beat_len = 50 % m + options.corr_len = 50 % m (kept) + options.manakov = 0 % 1=Manakov + options.SS_dphimax = 5e-3 % rad + options.SS_dzmax = 2e4 % m + options.SS_dzmin = 100 % m + options.n_waveplates = 100 + end + + % Copy provided options into properties + fn = fieldnames(options); + for n = 1:numel(fn) + obj.(fn{n}) = options.(fn{n}); + end + + % Initialize empty state; will be built on first process_ call + obj.state = struct(); + end + + function signalclass_out = process(obj, signalclass_in) + % Public entry point: takes a signal class with .signal (2xN) + % and writes back the propagated signal. + + signalclass_in.signal = obj.process_(signalclass_in.signal, signalclass_in.fs); + + % logbook (kept as in new framework skeleton) + lbdesc = 'DP_Fiber propagation (CNLSE_plain)'; + if ismethod(signalclass_in, 'logbookentry') + signalclass_in = signalclass_in.logbookentry(lbdesc); + end + + signalclass_out = signalclass_in; + end + + function signal_out = process_(obj, signal_in,fs) + % Core processing — builds legacy 'state' and calls CNLSE_plain + % data_in: [2 x N] complex, dual-pol envelope + arguments (Input) + obj + signal_in + fs + end + + % ---- Basic checks + if isempty(signal_in) || size(signal_in,2) ~= 2 + error('DP_Fiber:Input','Expected data_in of size [2 x N].'); + end + if isempty(fs) + error('DP_Fiber:Config','Sampling frequency options.fa is required.'); + end + + obj.fa = fs; + + % ---- RNG (legacy behavior) + R = RandStream("twister","Seed",obj.rng); + + % ---- (Re)build state if empty or size-dependent fields changed + need_rebuild = ~isfield(obj.state,'nt') || (obj.state.nt ~= size(signal_in,2)); + + if need_rebuild + % Constants + c0 = 299792458; % [m/s] + + % Legacy state mapping + st = struct(); + + % High-level + st.L = obj.L * 1000; % [m] legacy expects meters + st.polNames = {'X','Y'}; + st.dt = 1/obj.fa; + st.nt = max(size(signal_in)); + st.omega = 2*pi*[(0:st.nt/2-1),(-st.nt/2:-1)]/(st.dt*st.nt); + st.lambda = obj.lambda * 1e-9; % [m] + st.D = obj.D * 1e-6; % ps/(nm*km) -> s/(nm*m) + st.Dpmd = obj.Dpmd * 1e-6; % ps/sqrt(km) -> s/sqrt(km) + st.Ds = obj.Ds * 1e3; % ps/(nm^2*km) -> s/(nm^2*m) + st.beat_len = obj.beat_len; + st.SS_dzmax = obj.SS_dzmax; + st.SS_dzmin = obj.SS_dzmin; + st.SS_dphimax= obj.SS_dphimax; + + st.chi = 0; % legacy placeholders + st.psi = 0; + + st.manakov = obj.manakov; % 1 if eq==2 (Manakov), 0 if eq==1 (CNLSE) + st.wave_plates = obj.n_waveplates; + + % Alpha (same X/Y) + st.alpha.X = obj.X_alpha; + st.alpha_lin.X = st.alpha.X/10*log(10)/1000; + st.alpha.Y = st.alpha.X; + st.alpha_lin.Y = st.alpha_lin.X; + + % Beta (dispersion) for X (Y mirrors X) + if ~any(obj.X_beta) + % Populate from (beat_len, D, Ds, lambda) like legacy + if obj.beat_len ~= 0 + b1 = pi/obj.beat_len; + else + b1 = 0; + end + b2 = 0; % PMD freq term handled elsewhere in legacy + b3 = -(st.lambda.^2/(2*pi*c0))*st.D; + b4 = (st.lambda^2/(2*pi*c0))^2*st.Ds + (2/st.lambda)*(st.lambda.^2/(2*pi*c0)).^2*st.D; + + st.beta.X = [b1, b2, b3, b4]; % keep 4 terms, legacy had 0 for beta0; b2 unused + % Note: legacy stored [b0,b1,b2,b3]? Here we mirror their usage. + % We follow their X_beta layout length=4. + else + st.beta.X = obj.X_beta; + end + st.beta.Y = st.beta.X; + + % Gamma + st.gamma = obj.gamma; + + % PMD / birefringence (legacy waveplate model) + st.corr_length = st.L / st.wave_plates; + + % DGD formula (Agrawal 1.1.18) + st.dgd = st.Dpmd * sqrt(st.L/1000); % Dpmd in s/sqrt(km), L in m -> convert: sqrt(m/1000) + % For exact legacy match, they used: state.dgd = Dpmd * sqrt(L) wisth L in meters and Dpmd already scaled. + % Using their pattern: + st.dgd = obj.Dpmd*1e-6 * sqrt(st.L); % match old: state.Dpmd already 1e-6*ps/sqrt(km); they used sqrt(L) with L [m] + + brf_multiplier = 1; + if st.Dpmd == 0 + brf_multiplier = 0; + end + + % Waveplate random parameters + st.pauli_mats.s0 = eye(2); + st.pauli_mats.s2 = [0 1; 1 0]; + st.pauli_mats.s3i = [0 1; -1 0]; + + st.brf.theta = (R.rand(st.wave_plates,1)*pi - 0.5*pi) * brf_multiplier; + st.brf.epsilon = 0.5*asin(R.rand(st.wave_plates,1)*2-1) * brf_multiplier; + + st.brf.stokes = NaN(st.wave_plates,3); + st.Ttest = zeros(1, st.wave_plates); + st.brf.matR = cell(st.wave_plates,1); + + for n=1:st.wave_plates + matRth = cos(st.brf.theta(n)) * st.pauli_mats.s0 - sin(st.brf.theta(n)) * st.pauli_mats.s3i; + matRepsilon = complex(cos(st.brf.epsilon(n))*st.pauli_mats.s0, sin(st.brf.epsilon(n))*st.pauli_mats.s2); + matR = matRth * matRepsilon; + + st.brf.matR{n} = matR; + + u1 = matR(1,1); + u2 = matR(1,2); + + st.Ttest(n) = abs(u1).^2 + abs(u2).^2; + st.brf.stokes(n,:) = [abs(u1).^2 - abs(u2).^2 , (u1) * conj(u2) + conj(u1) .* u2 , 1i*(u1) * conj(u2) - conj(u1) .* u2]; + end + + % Frequency-dependent PMD phase term (legacy form) + st.brf.db0 = (R.rand(st.wave_plates,1)*2*pi - pi) * brf_multiplier; + st.brf.db1 = sqrt(3*pi/8)*(st.dgd/obj.fa)/st.wave_plates .* st.omega; + st.brf.simdgd = 0; + % cumsum used in legacy only for debug; keep compatibility variable: + ~cumsum(st.brf.db0); % no-op to mirror legacy path + + % Bookkeeping + st.missing_dz = 0; + st.n_plates_done = 0; + st.test_plates = []; + st.test_plate_numbers = []; + st.lin_z_test = 0; + st.propagated_length = 0; + + % Cache + obj.state = st; + end + + % ---- Call the exact same CNLSE_plain as in the old framework + x_in = signal_in(:,1).'; + y_in = signal_in(:,2).'; + + [x_out, y_out, obj.state] = CNLSE_plain(x_in, y_in, obj.state); + + obj.state.propagated_length = obj.state.propagated_length + obj.state.L; + + signal_out = [x_out; y_out].'; + + + end + end +end diff --git a/Classes/02_optical/EML.m b/Classes/02_optical/EML.m index 96765da..32a902c 100644 --- a/Classes/02_optical/EML.m +++ b/Classes/02_optical/EML.m @@ -70,7 +70,7 @@ classdef EML [signalclass_in.signal,obj] = obj.process_(signalclass_in.signal); % cast the inform. signal to electrical signal - signalclass_in = Opticalsignal(signalclass_in,"fs",obj.fsimu,"logbook",signalclass_in.logbook,"lambda",obj.lambda*1e-9,"nase",0); + signalclass_in = Opticalsignal(signalclass_in,"fs",obj.fsimu,"logbook",signalclass_in.logbook,"lambda",obj.lambda*1e-9,"nase",0,"polrot",0); % append to logbook lbdesc = [num2str(obj.lambda),' nm Laser with ',num2str(obj.power),' dBm P_out. Linew.=',num2str(obj.linewidth*1e-6),' MHz. Modulation mode: ',char(obj.mode) ]; diff --git a/Classes/02_optical/Optical_Demultiplex.m b/Classes/02_optical/Optical_Demultiplex.m new file mode 100644 index 0000000..fc2bc36 --- /dev/null +++ b/Classes/02_optical/Optical_Demultiplex.m @@ -0,0 +1,122 @@ +classdef Optical_Demultiplex < handle + % Dual-Polarization optical demultiplexer + % - Input: total-field signal + % - Output: single-channel dual-pol signal objects in cell array + % + % Notes: + % Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",Opt_sig_wdm_rx.fs/4,"fs_in",Opt_sig_wdm_rx.fs,"lambda_center",1310).process(Opt_sig_wdm_rx); + % Opt_sig_wdm_demux{1}.spectrum("fignum",1100,"displayname",'bla','normalizeTo0dB',0,'max_num_lines',4); + % Opt_sig_wdm_demux{2}.spectrum("fignum",1100,"displayname",'bla','normalizeTo0dB',0,'max_num_lines',4); + + properties (Access=public) + fs_in % [Hz] (optional; inferred from data_in.fs if omitted) + fs_out % [Hz] + lambda_center % [nm] center wavelength of the WDM grid + wavelengthplan % [nm] + attenuation = 0 % [dB] insertion loss + filtype = 1 % 1=Gaussian, 2=Rectangle, 3=No filter + B = 200e9 % [Hz] 3 dB bandwidth (Gaussian) or width (Rect) + mgauss = 3 % Gaussian order (multiple of 1/2) + + % Derived/utility + c = physconst('lightspeed') % [m/s] + end + + methods (Access=public) + function obj = Optical_Demultiplex(options) + arguments + options.fs_in = [] + options.fs_out + options.lambda_center + options.wavelengthplan + options.attenuation = 0 + options.filtype = 1 + options.B = 2.5e10 + options.mgauss = 3 + end + fn = fieldnames(options); + for n = 1:numel(fn) + try obj.(fn{n}) = options.(fn{n}); end + end + end + + function signalclasses_out = process(obj, signalclass_in) + + % ---- Infer wavelength: either given or from input total signal + if isempty(obj.wavelengthplan) + obj.wavelengthplan = signalclass_in.lambda; %meter + else + if all(500e-9 < obj.wavelengthplan) && all(obj.wavelengthplan < 1500e-9) %check if given in nm + obj.wavelengthplan = obj.wavelengthplan.*1e-9; + end + end + + % ---- Infer input sampling rates + if isempty(obj.fs_in) + assert(isprop(signalclass_in,'fs') && ~isempty(signalclass_in.fs), ... + 'Dual_Pol_Demultiplexer: data_in.fs missing and options.fs_in not provided.'); + obj.fs_in = signalclass_in.fs; + end + + % Runs demultiplexing in one go and appends a logbook entry. + [x_envelopes,y_envelopes] = obj.process_(signalclass_in.signal); + + for n = 1:min(size(x_envelopes)) + signalclasses_out{n} = signalclass_in; + signalclasses_out{n}.signal = [x_envelopes(:,n), y_envelopes(:,n)]; + signalclasses_out{n} = signalclasses_out{n}.resample("fs_in",obj.fs_in,"fs_out",obj.fs_out); + signalclasses_out{n}.lambda = obj.wavelengthplan(n); + lbdesc = ['Opt. Demux ', num2str( obj.wavelengthplan(n)),' nm']; + signalclasses_out{n} = signalclasses_out{n}.logbookentry(lbdesc); + end + + end + + function [x_envelopes,y_envelopes] = process_(obj, signal_in) + % Core demux: + % - frequency translate target channel to baseband + % - apply optical filter H + % - resample to fs_out + arguments (Input) + obj + signal_in + end + + w = obj.fs_out ./ obj.fs_in ; + blocklen_in = length(signal_in); + blocklen_out = w*blocklen_in; + + att = 1/10^(obj.attenuation/10); + faxis=linspace( -obj.fs_in/2 , obj.fs_in/2 , blocklen_in+1 ); + faxis=ifftshift(faxis(1:end-1)); + + switch obj.filtype + case 1 + H=exp(-(faxis/obj.B).^(2*obj.mgauss)*log(2)*2^(2*obj.mgauss-1)).'; + case 2 + %all zero filter + H=zeros(1,length(faxis)).'; + %set filter = 1 inside bandwidth -B/2 <-> B/2 + H(abs(faxis)<=obj.B/2)=1; + case 3 + H = 1; + end + + f_mid = obj.c/(obj.lambda_center*1e-9); % center frequency of WDM grid [Hz] + + f_channels = obj.c./(obj.wavelengthplan) ; + + N = numel(f_channels); + + df_T = f_mid - f_channels; + + pha = mod(-2*pi*(0:blocklen_in-1).'.*df_T/obj.fs_in, 2*pi); + lo = cos(pha)+1i*sin(pha); + + x_envelopes = ifft(fft(att.*signal_in(:,1).*lo).*H); + y_envelopes = ifft(fft(att.*signal_in(:,2).*lo).*H); + + end + end +end + diff --git a/Classes/02_optical/Optical_Multiplex.m b/Classes/02_optical/Optical_Multiplex.m new file mode 100644 index 0000000..1d722b0 --- /dev/null +++ b/Classes/02_optical/Optical_Multiplex.m @@ -0,0 +1,171 @@ +classdef Optical_Multiplex < handle + % Takes a cell array of signals + % returns a total field signal + % WDM spacing is given in wavelength plan OR via delta_F + + % The grid is stored in the output signal -> the demux will ideally + % look this up and use this as the demux frequencies... + + % signal_cell = {Opt_sig_1, Opt_sig_2}; + % Opt_sig_wdm = Optical_Multiplex("fs_in",Opt_sig.fs,"fs_out",4*Opt_sig.fs,... + % "lambda_center",1310,"random_key",0,"filtype",1,"B",200e9,"delta_f",400e9).process(signal_cell); + + properties(Access=public) + fs_in + fs_out + lambda_center + delta_f + random_key + attenuation + B + mgauss + filtype + + c = physconst('lightspeed') + f_center + f_T + lambda_T + df_T + end + + methods (Access=public) + function obj = Optical_Multiplex(options) + %NAME Construct an instance of this class + % Detailed explanation goes here + + arguments + options.fs_in + options.fs_out + options.lambda_center + options.B = 200e9 + options.mgauss = 3 + options.filtype = 2 + + options.delta_f = 0 + options.random_key + options.attenuation = 0; + end + + % + fn = fieldnames(options); + for n = 1:numel(fn) + try + obj.(fn{n}) = options.(fn{n}); + end + end + + + end + + function signalclass_out = process(obj,signalclasses_in) + + % actual processing of the signal (steps 1. - 3.) + signalclass_out = obj.process_(signalclasses_in); + + % append to logbook + lbdesc = ['Opt. Mux. ']; + signalclass_out = signalclass_out.logbookentry(lbdesc); + + end + + function data_out = process_(obj,data_in) + %METHOD1 Summary of this method goes here + % Detailed explanation goes here + arguments(Input) + obj + data_in cell + end + + % assert(data_in{1}.fs == obj.fs_in,'Sampling rate'); + att = 1/10^(obj.attenuation/10); + N = numel(data_in); + w = obj.fs_out/data_in{1}.fs; + blocklen_in = length(data_in{1}); + blocklen_out = w*blocklen_in; + freqaxis = linspace(-obj.fs_out/2, obj.fs_out/2, blocklen_out+1); + obj.f_center = obj.c/(obj.lambda_center.*1e-9); + + if obj.random_key ~= 0 + res = freqaxis(2)-freqaxis(1); + R = RandStream("twister","Seed",obj.random_key); + laser_frequency_imperfection = res .* round(R.randn(N,1)*10); %in mutliples of the fft resolution, i.e. the distance between two freq. bins + else + laser_frequency_imperfection = zeros(blocklen_in,1); + end + + obj.f_T = []; + obj.df_T = []; + polrots = []; + for o = 1:N + + if obj.delta_f ~= 0 + % user defined a channel spacing in GHz. Build plan + % left and right from zero + obj.df_T(o) = (-length(data_in)/2-0.5+o) .* obj.delta_f; + obj.df_T(o) = obj.df_T(o)+ laser_frequency_imperfection(o); + + obj.f_T = [obj.f_T obj.f_center+obj.df_T(o)]; + else + %center frequencies of channels + obj.f_T = [obj.f_T obj.c/(data_in{o}.lambda)]; + obj.lambda_T = [obj.lambda_T data_in{o}.lambda]; + + %difference between mid frequency of MUX and channels + obj.df_T = [obj.df_T obj.f_center - obj.f_T(o)]; + end + + % adapt frequency shifts to match the FFT grid! Find nearest grid point + [glitch(o),pos] = min(abs( freqaxis-obj.df_T(o) )); + obj.df_T(o) = freqaxis(pos); + + polrots = [polrots, data_in{o}.polrot]; + end + + obj.lambda_T = obj.c ./ (obj.f_center-obj.df_T); + + obj.B = 200e9; %200GHz + faxis = linspace(-obj.fs_out/2,obj.fs_out/2, blocklen_out+1);%generates arow vector faxis of blocklen+1 points linearly spaced between and including -para.fs/2 and para.fs/2 + faxis = ifftshift(faxis(1:end-1)); + + switch obj.filtype + case 1 + H =exp(-(faxis/obj.B).^(2*obj.mgauss)*log(2)*2^(2*obj.mgauss-1)).'; + case 2 + H=zeros(length(faxis),1); + H(find(abs(faxis)<=obj.B/2))=1; + case 3 + H = 1; + end + + x_envelopes = NaN([blocklen_out N]); + y_envelopes = x_envelopes; + + for o = 1:N + + pha = mod(2*pi*(0:blocklen_out-1)*obj.df_T(o)/obj.fs_out,2*pi).'; + lo = cos(pha)+1i*sin(pha); + data_in_resampled = data_in{o}.resample("fs_out",obj.fs_out); + + res_env = ifft(fft(data_in_resampled.signal(:,1)).*H); + x_envelopes(:,o) = att.*res_env.*lo; + + res_env = ifft(fft(data_in_resampled.signal(:,2)).*H); + y_envelopes(:,o) = att.*res_env.*lo; + + end + + data_out = data_in_resampled; + data_out.signal = [sum(x_envelopes,2), sum(y_envelopes,2)]; + data_out.lambda = obj.lambda_T; + data_out.polrot = polrots; + end + + end + + methods (Access=private) + % Cant be seen from outside! So put all your functions here that can/ + % shall not be called from outside + + + end +end diff --git a/Classes/02_optical/Polarization_Controller.m b/Classes/02_optical/Polarization_Controller.m new file mode 100644 index 0000000..62792b9 --- /dev/null +++ b/Classes/02_optical/Polarization_Controller.m @@ -0,0 +1,92 @@ +classdef Polarization_Controller + %Input can be "normal" - output will be DP! + + properties(Access=public) + + mode + desired_angle + desired_power + + rotation_angle + rotation_matrix + + end + + methods (Access=public) + function obj = Polarization_Controller(options) + %NAME Construct an instance of this class + % Detailed explanation goes here + + arguments + options.mode polarization_control_mode = polarization_control_mode.rot_power + options.desired_angle + options.desired_power + + end + + % + fn = fieldnames(options); + for n = 1:numel(fn) + try + obj.(fn{n}) = options.(fn{n}); + end + end + + % do more stuff + + end + + function signalclass_out = process(obj,signalclass_in) + + % actual processing of the signal (steps 1. - 3.) + [signalclass_in.signal,signalclass_in.polrot] = obj.process_(signalclass_in.signal,signalclass_in.polrot); + + % append to logbook + lbdesc = ['Logbookentry']; + signalclass_in = signalclass_in.logbookentry(lbdesc); + + % write to output + signalclass_out = signalclass_in; + + end + + function [data_out, polrot_out] = process_(obj,data_in,polrot_in) + % Rotate polarization of am opt signal + + arguments(Input) + obj + data_in double + polrot_in double + end + + + if obj.mode ~= polarization_control_mode.deactivate + + switch obj.mode + case polarization_control_mode.random + obj.rotation_angle = 2*pi*rand ; + + case polarization_control_mode.rot_angle + obj.rotation_angle = obj.desired_angle*pi/180 ; + + case polarization_control_mode.rot_power + obj.rotation_angle = -polrot_in + acos(sqrt(obj.desired_power/100)) ; + end + + obj.rotation_matrix = [cos(obj.rotation_angle) -sin(obj.rotation_angle) ; sin(obj.rotation_angle) cos(obj.rotation_angle)].' ; + + if min(size(data_in)) == 1 + data_in = reshape(data_in,[],1); + data_in = [data_in, zeros(length(data_in),1)]; + end + + data_out = data_in * obj.rotation_matrix; + + polrot_out = polrot_in + obj.rotation_angle ; + + end + + + end + end +end diff --git a/Classes/04_DSP/Coding/Duobinary.m b/Classes/04_DSP/Coding/Duobinary.m index 6f3460b..8590596 100644 --- a/Classes/04_DSP/Coding/Duobinary.m +++ b/Classes/04_DSP/Coding/Duobinary.m @@ -53,6 +53,7 @@ classdef Duobinary % bk(k+1) =mod(data(k)-bk(k),M); % end + % THIS WAS USED! for k = 2:numel(data) bk(k) = mod(data(k)-bk(k-1),M); end diff --git a/Classes/04_DSP/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index 3883877..e51d61b 100644 --- a/Classes/04_DSP/Sequence Detection/MLSE.m +++ b/Classes/04_DSP/Sequence Detection/MLSE.m @@ -29,9 +29,6 @@ classdef MLSE < handle obj.(fn{n}) = options.(fn{n}); end end - - % do more stuff - end function [signalclass_hd,LLR,GMI] = process(obj,signalclass,ref_symbolclass) @@ -57,6 +54,26 @@ classdef MLSE < handle function [VITERBI_ESTIMATION_SYMBOLS,LLR_maxlogmap,GMI] = process_(obj,data_in,data_ref) + debug = 0; + + 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) + % 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!) + + scale_mode = 2; % scale_mode: + % 0 = no scaling, + % 1 = RMS→scale MODEL, + % 2 = MMSE/time-corr→scale MODEL, + % 3 = RMS→scale DATA, + % 4 = MMSE/time-corr→scale DATA + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%% PREPARATIONS %%%%%%%% % remove unnecessary zeros at start of impulse response to keep % number of trellis states minimal @@ -69,22 +86,42 @@ classdef MLSE < handle obj.DIR = [0 obj.DIR]; end - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%%% PREPARATIONS %%%%%%%% - - %%%% Separate the equalized signal into the respective levels based on the actually transmitted level - constellation = unique(data_ref); - decisionLevels = (constellation(1:end-1) + constellation(2:end)) / 2; - N = length(data_in); - - tx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(data_ref); - % impulse respnse to remove from signal obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000 - % Normalize the Trellis states to =1 RMS - obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states); + tx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(data_ref); + + % Trellis States + 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 @@ -97,74 +134,84 @@ classdef MLSE < handle states = sum(combs,2); 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 - noise_free_received = zeros(nStates,nStates); - count_row = 1; - count_col = 1; - for l1 = 1:nStates - for l2 = 1:nStates - if sum(combs(l2,2:end) == combs(l1,1:end-1)) == size(combs,2)-1 - noise_free_received(count_row,count_col) = sum(combs(l2,:).*obj.DIR(end:-1:2)) + last_sym(l1)*obj.DIR(1); - else - noise_free_received(count_row,count_col) = inf; + % % 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 - count_row = count_row + 1; end - count_col = count_col + 1; - count_row = 1; end - % Was used earlier from Tom Wettlin etc. Sufficient for Viterbi - % but the BCJR is sensitive to the scaling, so I use another - % apporach - % % first: RMS normalization of input data (rms==1) - % data_in = data_in ./ rms(data_in); - % - % % then, match amplitude levels of input signal to those of the calculated ideal symbols - % % i.e. match the rms values of data_in to noise_free_received (rms=1.xx) - % if isreal(data_in) - % if obj.M == round(obj.M) - % data_in = data_in * rms(noise_free_received(noise_free_received ~= inf),'all','omitnan'); - % end - % end - - % quick and dirty alignment with xcorr - y = data_in; - x = data_ref; h = flip(obj.DIR(:)).'; - y_ideal = conv(x, h, "same"); - [c,lags] = xcorr(y, y_ideal, 64); % small max lag is enough - [~,ix] = max(abs(c)); - lag = lags(ix); - y_ideal = circshift(y_ideal, lag); + data_in = data_in(:); + y_ideal = conv(data_ref(:), h, "same"); - % center, skip transients, rm mean - skip = max(100, numel(obj.DIR)+50); - y_prep = y(1+skip:end-skip) - mean(y(1+skip:end-skip)); - y_ideal_prep = y_ideal(1+skip:end-skip) - mean(y_ideal(1+skip:end-skip)); - - % one-tap LS/MMSE gain and optional bias - g = (y_ideal_prep' * y_prep) / (y_ideal_prep' * y_ideal_prep); % complex allowed - b = mean(data_in) - g*mean(y_ideal); % intercept (if you keep means) - - dp = dot(y_ideal_prep, y_prep - g*y_ideal_prep); %shall be close to zero - - sigma2 = mean(abs(y_prep - g*y_ideal_prep).^2); - inv2s2 = 1/(2*sigma2); - - debug = 0; - if debug - figure(100); hold on - plot(1:length(y),y,'DisplayName','Y: Received Signal','LineStyle','none','Marker','.','MarkerSize',1); - plot(1:length(y_ideal),y_ideal,'DisplayName','Y ideal: x * DIR','LineStyle','none','Marker','.','MarkerSize',1); - yline(noise_free_received(:),'DisplayName','All Transition States','HandleVisibility','off','Color','red'); - yline(g*noise_free_received(:)+ b,'DisplayName','Scaled Transition States','HandleVisibility','off'); + 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 - noise_free_received = (g*noise_free_received + b); + % apply (g,b) to model and compute common sigma + noise_free_received = g*noise_free_received + b; + last_sym = g*last_sym + b; + sigma2 = mean(abs(data_in - (g*y_ideal + b)).^2); + inv2s2 = 1/(2*sigma2); + + if debug + figure(100); clf; hold on + showLevelScatter(data_in, data_ref, "fignum", 100); + 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) %%%%% @@ -185,8 +232,14 @@ classdef MLSE < handle for n = 2:length(data_in) bm = -(data_in(n) - noise_free_received).^2 * inv2s2; + + % exclude edge→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 + [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; @@ -202,6 +255,18 @@ classdef MLSE < handle 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); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -219,6 +284,12 @@ classdef MLSE < handle for h = length(data_in)-1:-1:1 bm = -(data_in(h+1) - noise_free_received).^2 * inv2s2; + + % exclude edge→edge transitions only 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) @@ -255,6 +326,7 @@ classdef MLSE < handle %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% FORWARD PASS PAM2,4,8 %%%%% + % These are interchangeable... second is chatgpt: 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) @@ -266,9 +338,6 @@ classdef MLSE < handle state_prob = exp(logPstate); % exact, sums to 1 - - - if obj.M == 6 num_bits = 5; @@ -357,17 +426,19 @@ classdef MLSE < handle for bit_idx = 1:num_bits % Find indices where bit is 0 and where it is 1 - idx_sym_1 = bit_mapping(:,bit_idx) == 0; + idx_bit_0 = bit_mapping(:,bit_idx) == 0; idx_bit_1 = bit_mapping(:,bit_idx) == 1; - % Sum over log-probabilities (Max-Log approximation: using max instead of sum) - LLR_maxlogmap(:,bit_idx) = max(LLP(idx_bit_1,:), [], 1) - max(LLP(idx_sym_1,:), [], 1); + % Sum over log-probabilities + % Max-Log approximation: using max instead of sum) + 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_sym_1, :),1); + 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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -377,12 +448,12 @@ classdef MLSE < handle LLR_exact = LLR_exact; for k = 1:num_bits - idx_bit_1 = (tx_bits(:,k) == 0); %wo sind die 1en - idx_sym_1 = (tx_bits(:,k) == 1); %wo sind die 0en + 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_1,k); - llr1 = LLR_exact(idx_sym_1,k); + llr0 = LLR_exact(idx_bit_0,k); + llr1 = LLR_exact(idx_bit_1,k); % Calculate mutual information for bit position k I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1 @@ -396,7 +467,6 @@ classdef MLSE < handle VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS); - if debug %%% DEBUG PLOT LIKELIHOOD RATIOS %%% figure(115);clf @@ -415,6 +485,8 @@ classdef MLSE < handle legend end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% CHECK BER's %%%%% @@ -430,6 +502,13 @@ classdef MLSE < handle fprintf('Viterbi BER: %.2e \n',ber_viterbi); fprintf('Viterbi Errors = %d\n', numErr); + 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 edges).\n', nnz(isforbidden)); + + % Convert LLR values to a hard-decision bit stream bit_stream = LLR_maxlogmap > 0; %ratio separates lower or higher than =0 -> simply decode for the negative values bit_stream = reshape(bit_stream',[],1); @@ -458,7 +537,7 @@ classdef MLSE < handle rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(FW_EST'); rx_bits = reshape(rx_bits',[],1); [~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('FW BER: %.2e \n',ber_fw); + fprintf('FW BER : %.2e \n',ber_fw); disp('Stop DEBUG MLSE:') fprintf('\n') end @@ -471,7 +550,5 @@ classdef MLSE < handle s = amax + log(sum(exp(a - amax), dim)); end - end - end diff --git a/Classes/04_DSP/TransmissionPerformance.m b/Classes/04_DSP/TransmissionPerformance.m index 1550e40..d87d691 100644 --- a/Classes/04_DSP/TransmissionPerformance.m +++ b/Classes/04_DSP/TransmissionPerformance.m @@ -42,6 +42,7 @@ classdef TransmissionPerformance 0.8733, 0.8790, 0.8848, 0.8905, 0.8962, ... 0.9019, 0.9077, 0.9134, 0.9191, 0.9248, ... 0.9306, 0.9363, 0.9420, 0.9477, 0.9535]; + NGMITHRESHOLDS_SDHD = [0.8116, 0.8167, 0.8241, 0.8317, 0.8401, ... 0.8459, 0.8512, 0.8574, 0.8685, 0.8746, ... 0.8829, 0.8892, 0.8958, 0.9022, 0.9090,... @@ -60,7 +61,7 @@ classdef TransmissionPerformance %% LUT for KP4-FEC and Inner Code https://grouper.ieee.org/groups/802/3/dj/public/23_03/patra_3dj_01b_2303.pdf - CODE_RATE_KP4_AND_INNER = [0.885799]; + CODE_RATE_KP4_AND_INNER = [0.885799]; %https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=9979198 -> 199.8 / 224 = 0.891 als code rate BERTHRESHOLDS_KP4_AND_INNER = 4.85e-3; % https://www.ieee802.org/3/bs/public/14_11/parthasarathy_3bs_01a_1114.pdf @@ -153,11 +154,21 @@ classdef TransmissionPerformance netrates.SDHD.Threshold = NaN(1, numMeasurements); end if ~isempty(ber) + netrates.STAIR.GrossRate = NaN(1, numMeasurements); + netrates.STAIR.NetRate = NaN(1, numMeasurements); + netrates.STAIR.CodeRate = NaN(1, numMeasurements); + netrates.STAIR.Threshold = NaN(1, numMeasurements); + netrates.HD.GrossRate = NaN(1, numMeasurements); netrates.HD.NetRate = NaN(1, numMeasurements); netrates.HD.CodeRate = NaN(1, numMeasurements); netrates.HD.Threshold = NaN(1, numMeasurements); + netrates.KP4.GrossRate = NaN(1, numMeasurements); + netrates.KP4.NetRate = NaN(1, numMeasurements); + netrates.KP4.CodeRate = NaN(1, numMeasurements); + netrates.KP4.Threshold = NaN(1, numMeasurements); + netrates.KP4_hamming.GrossRate = NaN(1, numMeasurements); netrates.KP4_hamming.NetRate = NaN(1, numMeasurements); netrates.KP4_hamming.CodeRate = NaN(1, numMeasurements); @@ -200,11 +211,47 @@ classdef TransmissionPerformance end if ~isempty(idxBER) codeRate = obj.CODE_RATES_HD(idxBER); + netrates.STAIR.NetRate(i) = grossRate(i) * codeRate; + netrates.STAIR.GrossRate(i) = grossRate(i) ; + netrates.STAIR.CodeRate(i) = codeRate; + netrates.STAIR.Threshold(i) = obj.BERTHRESHOLDS_HD(idxBER); + end + + % HD FEC 3,8e-3 + idxBER = []; + for j = length(obj.BERTHRESHOLDS_HDFEC):-1:1 + if ber(i) <= obj.BERTHRESHOLDS_HDFEC(j) + idxBER = j; + break; + end + end + if ~isempty(idxBER) + codeRate = obj.CODE_RATE_HDFEC(idxBER); netrates.HD.NetRate(i) = grossRate(i) * codeRate; netrates.HD.GrossRate(i) = grossRate(i) ; netrates.HD.CodeRate(i) = codeRate; - netrates.HD.Threshold(i) = obj.BERTHRESHOLDS_HD(idxBER); + netrates.HD.Threshold(i) = obj.CODE_RATE_HDFEC(idxBER); end + + + % KP4 + idxBER = []; + for j = length(obj.BERTHRESHOLDS_KP4):-1:1 + if ber(i) <= obj.BERTHRESHOLDS_KP4(j) + idxBER = j; + break; + end + end + if ~isempty(idxBER) + codeRate = obj.CODE_RATE_KP4(idxBER); + netrates.KP4.NetRate(i) = grossRate(i) * codeRate; + netrates.KP4.GrossRate(i) = grossRate(i) ; + netrates.KP4.CodeRate(i) = codeRate; + netrates.KP4.Threshold(i) = obj.CODE_RATE_KP4(idxBER); + end + + + % KP4 + inner Hamming idxBER = []; for j = length(obj.BERTHRESHOLDS_KP4_AND_INNER):-1:1 if ber(i) <= obj.BERTHRESHOLDS_KP4_AND_INNER(j) @@ -220,6 +267,7 @@ classdef TransmissionPerformance netrates.KP4_hamming.Threshold(i) = obj.BERTHRESHOLDS_KP4_AND_INNER(idxBER); end + % O FEC idxBER = []; for j = length(obj.BERTHRESHOLDS_O_FEC):-1:1 if ber(i) <= obj.BERTHRESHOLDS_O_FEC(j) diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index d21d3be..f1ca74e 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -63,7 +63,9 @@ classdef DBHandler < handle obj.refresh(); else + error('DB seems to be corrupt') + end end @@ -183,6 +185,7 @@ classdef DBHandler < handle fprintf('Raw Rx Paths: Found %d duplictaes of %s \n',duplictae_raw.occurrences(i),duplictae_raw.rx_raw_path(i)); end end + healthyDB = true; end @@ -713,68 +716,167 @@ classdef DBHandler < handle selectedFields end - % Step 1: Handle selectedFields conversion + % -------- Step 1: Normalize selectedFields to {'Table.field', ...} -------- if isempty(selectedFields) || (ischar(selectedFields) && strcmpi(selectedFields, 'all')) - selectedFields = obj.getTableFieldNames('Runs'); % Default to Runs table + selectedFields = obj.getTableFieldNames('Runs'); % default elseif isstruct(selectedFields) - % Convert struct to cell array of 'Table.field' format newFields = {}; tableNames = fieldnames(selectedFields); for t = 1:numel(tableNames) tableStruct = selectedFields.(tableNames{t}); - fieldNames = fieldnames(tableStruct); - for f = 1:numel(fieldNames) - if isequal(tableStruct.(fieldNames{f}), 1) - newFields{end+1} = sprintf('%s.%s', tableNames{t}, fieldNames{f}); + fns = fieldnames(tableStruct); + for f = 1:numel(fns) + if isequal(tableStruct.(fns{f}), 1) + newFields{end+1} = sprintf('%s.%s', tableNames{t}, fns{f}); %#ok end end end selectedFields = newFields; end - % Step 2: Generate COALESCE string for SELECT clause + % Parse the table names actually referenced by the SELECT + reqTables = unique(cellfun(@(s) extractBefore(s, '.'), selectedFields, ... + 'UniformOutput', false)); + + % -------- Step 2: Build SELECT with COALESCE wrapper as you already do ---- selectClause = obj.generateCoalesceString(selectedFields); - % Step 3: Generate FROM clause with joins - mainTable = 'Runs'; - fromClause = ['FROM ', mainTable, ' ']; + % -------- Step 3: FROM and minimal JOIN plan ------------------------------ + % Decide main table: prefer the first explicitly referenced table, else 'Runs' + if ~isempty(reqTables) + mainTable = reqTables{1}; + else + mainTable = 'Runs'; + end - % Prepare join buffers - normalJoins = ''; - equalizerJoin = ''; - - tableNamesAll = fieldnames(obj.tables); - for t = 1:numel(tableNamesAll) - tableName = tableNamesAll{t}; - - if strcmpi(tableName, mainTable) || strcmpi(tableName, 'sqlite_sequence') - continue; - end - - if isfield(obj.tables.(tableName), 'run_id') - % Direct join to Runs - normalJoins = [normalJoins, 'LEFT JOIN ', tableName, ... - ' ON ', mainTable, '.run_id = ', tableName, '.run_id ']; - elseif isfield(obj.tables.(tableName), 'eq_id') - % Equalizer depends on Results, collect this join separately - equalizerJoin = ['LEFT JOIN ', tableName, ... - ' ON Results.eq_id = ', tableName, '.eq_id ']; + % If WHERE references a table not in reqTables (e.g., Runs.*), make sure it’s present. + whereClause = ''; + if ~isempty(filterParams) + whereClause = obj.generateWhereClause(filterParams); + % Heuristic: add 'Runs' if WHERE clause mentions 'Runs.' + if contains(whereClause, 'Runs.') + reqTables = unique([reqTables; {'Runs'}]); %#ok end end - % Combine joins: normal joins first, Equalizer last - fromClause = [fromClause, normalJoins, equalizerJoin]; + % Ensure main table is included + if ~ismember(mainTable, reqTables) + reqTables = unique([mainTable; reqTables]); %#ok + end - % Step 4: Generate WHERE clause if filter parameters exist - if ~isempty(filterParams) - whereClause = obj.generateWhereClause(filterParams); - if ~isempty(whereClause) - query = [selectClause, ' ', fromClause, 'WHERE ', whereClause]; - else - query = [selectClause, ' ', fromClause]; + % We’ll build joins only for the required tables (minus the main) + otherTables = setdiff(reqTables, {mainTable}); + + % Keep track of what’s already in the FROM graph (start with main) + present = string(mainTable); + joins = strings(0,1); + + % Helper lambdas + hasField = @(tbl, fld) isfield(obj.tables.(char(tbl)), char(fld)); + canJoinBy = @(left, right, key) hasField(left, key) && hasField(right, key); + + + % A small helper that adds a LEFT JOIN if the right table isn't present yet + function addJoinByKey(rightTbl, key) + if any(present == string(rightTbl)) + return; % already joined end + % Prefer to join against an already-present table that has the key + anchor = ''; + for k = 1:numel(present) + if canJoinBy(char(present(k)), rightTbl, key) + anchor = char(present(k)); + break; + end + end + if isempty(anchor) + % No anchor in current graph; if the right table is 'Equalizer' and key is eq_id, + % try to ensure a bridge table with eq_id exists (Results or a dashboard view). + if strcmpi(rightTbl,'Equalizer') && strcmpi(key,'eq_id') + % Bring in one eq_id-capable table if it is requested + bridgeOrder = {'Results','dashboard_old','dashboard_new','dashboard_ungrouped'}; + for b = 1:numel(bridgeOrder) + br = bridgeOrder{b}; + if ismember(br, reqTables) && ~any(present == string(br)) && hasField(obj.tables.(br),'eq_id') + % Attach bridge by run_id if possible, otherwise leave for eq_id + if any(present == "Runs") && hasField(obj.tables.(br),'run_id') && hasField(obj.tables.('Runs'),'run_id') + joins(end+1,1) = "LEFT JOIN " + br + " ON Runs.run_id = " + br + ".run_id"; + present(end+1,1) = string(br); + anchor = br; % we can now anchor Equalizer on eq_id to this + break; + else + % Fallback: anchor to main if it shares eq_id + for k = 1:numel(present) + pk = char(present(k)); + if canJoinBy(pk, br, 'eq_id') + joins(end+1,1) = "LEFT JOIN " + br + " ON " + pk + ".eq_id = " + br + ".eq_id"; + present(end+1,1) = string(br); + anchor = br; + break; + end + end + if ~isempty(anchor), break; end + end + end + end + end + end + % Re-scan for an anchor (maybe the bridge helped) + if isempty(anchor) + for k = 1:numel(present) + if canJoinBy(char(present(k)), rightTbl, key) + anchor = char(present(k)); + break; + end + end + end + if isempty(anchor) + % As a final fallback, if the main table is Runs and right has run_id, join by run_id + if strcmpi(mainTable,'Runs') && hasField(obj.tables.(rightTbl),'run_id') && hasField(obj.tables.('Runs'),'run_id') + anchor = 'Runs'; + key = 'run_id'; + end + end + if isempty(anchor) + % Could not find a path; skip join silently (or throw if you prefer strict) + return; + end + joins(end+1,1) = "LEFT JOIN " + rightTbl + " ON " + anchor + "." + key + " = " + rightTbl + "." + key; + present(end+1,1) = string(rightTbl); + end + + % First pass: if WHERE uses Runs.* and mainTable isn’t Runs, ensure Runs is in the graph + if contains(string(whereClause), "Runs.") && ~any(present == "Runs") + % Try to join Runs to whatever has run_id (mainTable ideally) + if hasField(mainTable, 'run_id') && hasField('Runs', 'run_id') + joins(end+1,1) = "LEFT JOIN Runs ON " + string(mainTable) + ".run_id = Runs.run_id"; + present(end+1,1) = "Runs"; + end + end + + % Join the required tables with minimal edges + for i = 1:numel(otherTables) + tbl = otherTables{i}; + % Prefer run_id join if possible, else eq_id, else skip + if any(present == "Runs") && hasField(tbl,'run_id') + addJoinByKey(tbl, 'run_id'); + elseif hasField(tbl,'run_id') && hasField(mainTable,'run_id') + addJoinByKey(tbl, 'run_id'); + elseif hasField(tbl,'eq_id') + addJoinByKey(tbl, 'eq_id'); + else + % no obvious key; skip + end + end + + % Build the FROM clause + fromClause = "FROM " + string(mainTable) + " " + strjoin(joins, " "); + + % -------- Step 4: WHERE (unchanged logic) ------------------------------ + if ~isempty(whereClause) + query = char(strjoin([selectClause, fromClause, "WHERE " + string(whereClause)], " ")); else - query = [selectClause, ' ', fromClause]; + query = char(strjoin([selectClause, fromClause], " ")); end end diff --git a/Classes/Warehouse_class/classes/DataStorage.m b/Classes/Warehouse_class/classes/DataStorage.m index f057c54..5fd288d 100644 --- a/Classes/Warehouse_class/classes/DataStorage.m +++ b/Classes/Warehouse_class/classes/DataStorage.m @@ -51,8 +51,9 @@ classdef DataStorage < handle function save(obj,path) try save(path,"obj"); - catch - + catch e + disp(e.message) + disp('Provide save path') end end diff --git a/Classes/Warehouse_class/classes/minimal_example.m b/Classes/Warehouse_class/classes/minimal_example.m new file mode 100644 index 0000000..b106929 --- /dev/null +++ b/Classes/Warehouse_class/classes/minimal_example.m @@ -0,0 +1,40 @@ + +params = struct; +params.sir = [20:2:36]; +params.laser_linewidth = [1e5 1e6 10e6]; +params.pn_key = [1:3]; +params.vp = [0.25,0.5,0.75,1]; +params.vb = [1:0.1:1.8]; +params.rop = -5:0; + +wh = DataStorage(params); +wh.addStorage("ber"); +wh.addStorage("rop_save"); +wh.addStorage("cspr"); +wh.addStorage("mod_out_pow"); + + +cnt = 1; +for sir = params.sir + for lw = params.laser_linewidth + for pnk = params.pn_key + for vp = params.vp + for vb = params.vb + for rop = params.rop + + current_ber = randn; + wh.addValueToStorage(current_ber,'ber',sir,lw,pnk,vp,vb,rop); + wh.addValueToStorage(-10,'rop_save',sir,lw,pnk,vp,vb,rop); + wh.addValueToStorage(10,'cspr',sir,lw,pnk,vp,vb,rop); + wh.addValueToStorage(-6,'mod_out_pow',sir,lw,pnk,vp,vb,rop); + + end + end + end + end + end +end + +wh.getStoValue('ber',20,1e5,2,0.25,1.1,-3) + +wh.showInfo \ No newline at end of file diff --git a/Datatypes/polarization_control_mode.m b/Datatypes/polarization_control_mode.m new file mode 100644 index 0000000..c968fcd --- /dev/null +++ b/Datatypes/polarization_control_mode.m @@ -0,0 +1,10 @@ +classdef polarization_control_mode < int32 + + enumeration + random (1) + rot_angle (2) + rot_power (3) + deactivate (4) + 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 a698314..09e5e27 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -96,11 +96,11 @@ try adaption= 1; use_dd_mode = 1; - use_ffe = 0; - use_dfe = 0; + use_ffe = 1; + use_dfe = 1; use_vnle_mlse = 1; use_dbtgt = 1; - use_dbenc = 1; + use_dbenc = 0; addProcessingResultToDatabase = 0; @@ -125,13 +125,12 @@ try % - 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); - mlse_db_ = MLSE("DIR",[1,1],"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); % 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, ... @@ -194,60 +193,78 @@ try if use_vnle_mlse 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); 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); + + 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); + end [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... "precode_mode", duob_mode,... - 'showAnalysis', 1, ... + 'showAnalysis', 0, ... "postFFE", [],... "eth_style_symbol_mapping", 0); ffe_results.metrics.print; ffe_results.config.equalizer_structure = "vnle"; 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 - 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); + % 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 - [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... - "precode_mode", duob_mode,... - 'showAnalysis', 1, ... - "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_dbtgt + useviterbi = 0; + 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); + 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, Scpe_sig, Symbols, Tx_bits, ... "precode_mode", duob_mode, ... 'showAnalysis', 0,... "postFFE", []); + dbt_results.metrics.print; + output.dbtgt_package{r} = dbt_results; if options.append_to_db diff --git a/Functions/EQ_structures/duobinary_target.m b/Functions/EQ_structures/duobinary_target.m index 687d6ca..3a1389f 100644 --- a/Functions/EQ_structures/duobinary_target.m +++ b/Functions/EQ_structures/duobinary_target.m @@ -23,8 +23,14 @@ if ~isempty(options.postFFE) end mlse_.DIR = [1,1]; -% mlse_sig_sd = mlse_.process(eq_signal); -[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); +% + +if isa(mlse_,'MLSE_viterbi') + mlse_sig_sd = mlse_.process(eq_signal); +else + [mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); +end + mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd); % precoding to mitigate error propagation, most prominently used in @@ -43,8 +49,8 @@ switch options.precode_mode 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 @@ -59,24 +65,42 @@ switch options.precode_mode 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",options.eth_style_symbol_mapping).demap(mlse_sig_hd_decoded); - [~,errors_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - + [~,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",options.eth_style_symbol_mapping).demap(tx_symbols); rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); - [bits_db,errors_db,ber_db,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + [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 % M = numel(unique(tx_symbols.signal)); rx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); [bits_db,errors_db,ber_db,errorIndice_db] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + + + + alpha = arburg(eq_noise.signal,1);%pf_.coefficients(2); alpha = alpha(2); -gmi_mlse = GMI_MLSE; -air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M)); +if isa(mlse_,'MLSE_viterbi') + gmi_mlse = NaN; + air_mlse = NaN; +else + gmi_mlse = GMI_MLSE; + air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M)); +end db_results = struct(); db_results.metrics = Metricstruct; diff --git a/Functions/EQ_structures/ffe.m b/Functions/EQ_structures/ffe.m index e3ecc04..1f927b1 100644 --- a/Functions/EQ_structures/ffe.m +++ b/Functions/EQ_structures/ffe.m @@ -49,7 +49,10 @@ eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); %% Calculate performance metrics [snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); -[gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); +% [gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); +[gmi] = calc_ngmi(eq_signal_sd,tx_symbols); +gmi = max(gmi,0); + air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M)); [evm_total, evm_lvl] = calc_evm(eq_signal_sd, tx_symbols); [std_total, std_lvl] = calc_std(eq_signal_sd, tx_symbols); diff --git a/Functions/EQ_structures/vnle_postfilter_mlse.m b/Functions/EQ_structures/vnle_postfilter_mlse.m index 2ad8ea2..b86dc93 100644 --- a/Functions/EQ_structures/vnle_postfilter_mlse.m +++ b/Functions/EQ_structures/vnle_postfilter_mlse.m @@ -34,7 +34,7 @@ end % FFE or VNLE [eq_signal_sd, eq_noise] = eq_.process(rx_signal, tx_symbols); -% Apply post-FFE if provided +% Apply post-FFE if provided (does not work properly at the moment...very sad) if ~isempty(options.postFFE) tic [eq_signal_sd, eq_noise] = options.postFFE.process(eq_signal_sd, tx_symbols); @@ -43,13 +43,18 @@ end % Hard decision on VNLE output eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); -eq_signal_hd.spectrum("displayname",'after full response FFE','fignum',2025,'normalizeTo0dB',1); % Process through postfilter and MLSE [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); mlse_.DIR = pf_.coefficients; -[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(mlse_sig_sd,tx_symbols); +GMI_MLSE = NaN; +if isa(mlse_,'MLSE_viterbi') + [mlse_sig_sd] = mlse_.process(mlse_sig_sd); +else + [mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(mlse_sig_sd,tx_symbols); +end + mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).quantize(mlse_sig_sd); %% Calculate BER based on precoding mode @@ -58,7 +63,12 @@ mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).qua %% Calculate performance metrics % VNLE metrics [snr_vnle, snr_vnle_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); -[gmi_vnle] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); +% [gmi_vnle] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); + +%calculate bitwise GMI +[gmi_vnle] = calc_ngmi(eq_signal_sd,tx_symbols); +gmi_vnle = max(gmi_vnle,0); + air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M)); [evm_vnle_total, evm_vnle_lvl] = calc_evm(eq_signal_sd, tx_symbols); [std_vnle_total, std_vnle_lvl] = calc_std(eq_signal_sd, tx_symbols); @@ -67,7 +77,7 @@ air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(dou % MLSE metrics alpha = arburg(eq_noise.signal,1);%pf_.coefficients(2); alpha = alpha(2); -gmi_mlse = GMI_MLSE; +gmi_mlse = max(GMI_MLSE,0); air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M)); %% Display analysis if requested @@ -77,6 +87,10 @@ end + + + + %% Prepare output structures % Determine postFFE order if ~isempty(options.postFFE) @@ -119,8 +133,6 @@ ffe_results.config.eq = jsonencode(eq_); ffe_results.config.equalizer_structure = int32(equalizer_structure.vnle); ffe_results.config.comment = 'function: vnle_postfilter_mlse - FFE part'; - - mlse_results = struct(); mlse_results.metrics = Metricstruct; mlse_results.metrics.result_id = NaN; @@ -141,7 +153,6 @@ mlse_results.metrics.Alpha = alpha; mlse_results.metrics.MLSE_dir = mlse_.DIR; % Create MLSE results structure - mlse_results.config = Equalizerstruct(); mlse_results.config.eq = jsonencode(eq_); mlse_.DIR = length(mlse_.DIR)-1; @@ -203,7 +214,7 @@ switch precode_mode 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 = mapper.demap(mlse_sig_hd_decoded); - [~, errors.mlse_precoded, ber.mlse_precoded, ~] = calc_ber(rx_bits_mlse_decoded.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); + [~, errors.mlse_precoded, ber.mlse_precoded, err_loc_precoded] = calc_ber(rx_bits_mlse_decoded.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); % B) Omit the Coding by comparing with demapped TX symbol sequence tx_bits_demapped = mapper.demap(tx_symbols); @@ -213,6 +224,19 @@ switch precode_mode rx_bits_mlse = mapper.demap(mlse_sig_hd); [numbits.mlse, errors.mlse, ber.mlse, error_locations.mlse] = calc_ber(rx_bits_mlse.signal, tx_bits_demapped.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1); end + +% cols = linspecer(8); +% burst_precoded = count_error_bursts(err_loc_precoded, 40); +% burst_normal = count_error_bursts(error_locations.mlse, 40); +% figure();hold on; +% stem(1:40,burst_normal,'LineWidth',1,'Color',cols(1,:),'Marker','_','DisplayName','w/o diff. precoder'); +% stem(1:40,burst_precoded,'LineWidth',1,'Color',cols(2,:),'Marker','.','LineStyle','-','DisplayName','w diff. precoder'); +% xlabel('Bit Error Burst Length') +% ylabel('Occurence') +% set(gca, 'yscale', 'log'); + + + end diff --git a/Functions/EQ_visuals/show2Dconstellation.m b/Functions/EQ_visuals/show2Dconstellation.m new file mode 100644 index 0000000..8596676 --- /dev/null +++ b/Functions/EQ_visuals/show2Dconstellation.m @@ -0,0 +1,85 @@ +function [symbols_for_lvl,avg_for_lvl] = show2Dconstellation(eq_signal,ref_symbols,options) +arguments + eq_signal + ref_symbols + options.fignum (1,1) double = NaN % Default to NaN if not provided + options.displayname (1,:) char = '' % Default to an empty string if not provided +end + +plot_shit = 1; + +if isa(eq_signal,'Signal') + eq_signal = eq_signal.signal; +end +if isa(ref_symbols,'Signal') + ref_symbols = ref_symbols.signal; +end + + +if plot_shit + % Determine the figure number to use or create a new figure + if isnan(options.fignum) + fig = figure; % Create a new figure and get its handle + else + fig = figure(options.fignum); % Use the specified figure number + clf; + end +end + + +rx_symbols = eq_signal; %./ rms(eq_signal); +correct_symbols = ref_symbols; + +col = cbrewer2('Paired',numel(unique(correct_symbols))*2); +ccnt = -1; + +levels = unique(correct_symbols); +M = numel(levels); +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); + + statistical_mean(l) = mean(symbols_for_lvl(l,:),'omitnan'); + +end + +tx_even = correct_symbols(1:2:end); +tx_odd = correct_symbols(2:2:end); + +rx_even = rx_symbols(1:2:end); +rx_odd = rx_symbols(2:2:end); + +D_even = tx_even - rx_even; +D_odd = tx_odd - rx_odd; + +D = sqrt(D_even.^2 + D_odd.^2); + +[X,Y] = meshgrid(levels, levels); +[X_,Y_] = meshgrid(statistical_mean, statistical_mean); + +hold on; +scatter(rx_even,rx_odd,5*ones(1,length(D)),D,'.','DisplayName',['Even/Odd PAM-',num2str(M)],'MarkerEdgeColor',col(2,:)); +% colormap(gca,flip(cbrewer2('Spectral',100))) +colormap(gca,'hsv'); +scatter(X_(:), Y_(:), 2, 'x', 'LineWidth', 10, 'MarkerEdgeColor', col(6,:),'DisplayName','Statistical Rx Levels'); +scatter(X(:), Y(:), 2, 'x', 'LineWidth', 10, 'MarkerEdgeColor', col(4,:),'DisplayName','Tx Levels'); +xlim([floor(min(X_(:)))-1, ceil(max(X_(:)))+1]); +ylim([floor(min(Y_(:)))-1, ceil(max(Y_(:)))+1]); + +yticks((levels(1:end-1) + levels(2:end)) / 2); +xticks((levels(1:end-1) + levels(2:end)) / 2); +legend +xlabel('even symbols'); +ylabel('odd symbols'); +axis equal; grid on; +end diff --git a/Functions/EQ_visuals/showLevelScatter.m b/Functions/EQ_visuals/showLevelScatter.m index 70ddb39..f39dcb8 100644 --- a/Functions/EQ_visuals/showLevelScatter.m +++ b/Functions/EQ_visuals/showLevelScatter.m @@ -4,7 +4,7 @@ arguments ref_symbols options.fignum (1,1) double = NaN % Default to NaN if not provided options.displayname (1,:) char = '' % Default to an empty string if not provided - options.f_sym = []; + options.f_sym =1e6; end plot_shit = 1; @@ -30,7 +30,7 @@ if plot_shit end -rx_symbols = eq_signal ./ rms(eq_signal); +rx_symbols = eq_signal; %./ rms(eq_signal); correct_symbols = ref_symbols; f_sym = options.f_sym; @@ -47,7 +47,6 @@ for l = 1:numel(levels) 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_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6; @@ -68,7 +67,8 @@ for l = 1:numel(levels) ccnt = ccnt+2; level_amplitude = levels(l); - movmean = 1/250 .* movsum(rx_symbols(correct_symbols==level_amplitude),[250/2,250/2], 'Endpoints', 'fill'); + 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; @@ -125,7 +125,7 @@ if 0 end if plot_shit -yline(levels); +% yline(levels); xlabel('Time in $\mu$s'); ylabel('Normalized Amplitude'); ylim([-3 3]); diff --git a/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m b/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m index 1b76fd4..c10f1d7 100644 --- a/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m +++ b/Functions/Job_Processing/loadAndSyncSignalDataFromDb.m @@ -12,12 +12,12 @@ function [Bits, Symbols, Scpe_cell, found_sync] = loadAndSyncSignalDataFromDb(da % found_sync - Boolean indicating if synchronization was successful found_sync = 0; -tempLocalStorage = 0; +tempLocalStorage = 1; % Define the fixed storage directory relative to the user's MATLAB preferences directory storage_dir = fullfile(prefdir, 'temp_sync_data'); -% Part A: Check and load from local storage if available +% Part A: Check and load from local storage if available- if tempLocalStorage == 1 local_filename = fullfile(storage_dir, sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id))); if exist(local_filename, 'file') diff --git a/Functions/Metrics/calc_snr.m b/Functions/Metrics/calc_snr.m index 2a06f49..ff25cd9 100644 --- a/Functions/Metrics/calc_snr.m +++ b/Functions/Metrics/calc_snr.m @@ -39,6 +39,5 @@ function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise) % Compute the SNR for these indices snr_per_level(i) = snr(tx_signal(idx), eq_noise(idx)); - histogram(eq_noise(idx)); end end diff --git a/Functions/Metrics/count_error_bursts.m b/Functions/Metrics/count_error_bursts.m index fdc5fc1..47d5439 100644 --- a/Functions/Metrics/count_error_bursts.m +++ b/Functions/Metrics/count_error_bursts.m @@ -21,8 +21,10 @@ function burst_count = count_error_bursts(err_pos, max_burst_length) % Check if the burst length exceeds any of the thresholds for i = 1:max_burst_length - if burst_length > i + if burst_length == i burst_count(i) = burst_count(i) + 1; + else + end end end diff --git a/projects/ECOC_2025/dsp_test/hyperparam_tuning.m b/projects/ECOC_2025/dsp_test/hyperparam_tuning.m index 75f415e..aec4165 100644 --- a/projects/ECOC_2025/dsp_test/hyperparam_tuning.m +++ b/projects/ECOC_2025/dsp_test/hyperparam_tuning.m @@ -6,10 +6,10 @@ Symbols = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_symbols.mat");S eq_ = FFE_DCremoval("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",1e-5,"mu_tr",0,"order",50,"sps",2,"decide",0,"mu_dc",0.005,"dc_buffer_len",1); -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"); +% 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"); params = (logspace(-6,-2,20)); params = floor((logspace(2,3,20))); @@ -19,16 +19,16 @@ for i = 1:length(params) eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,... - "mu_dc",0.005,"dc_buffer_len",224, ... + "mu_dc",0.005,"dc_buffer_len",1, ... "ffe_buffer_len",1,... "smoothing_buffer_length",0,... - "smoothing_buffer_update",224); + "smoothing_buffer_update",1); - result = vnle(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0); - ber_ffe(i) = result.ber_vnle; + result = ffe(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0); + ber_ffe(i) = result.metrics; fprintf(" FFE Results: %.2e\n", ber_ffe(i)); - db.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE); + % db.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE); % eq_ = FFE_adaptive_decision("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",... % 0.0003,"mu_tr",0,"order",50,"sps",2,"decide",1,"buffer_length",params(i)); 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 3b2696c..297f17b 100644 --- a/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m +++ b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m @@ -1,25 +1,24 @@ -basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +% basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; % database = DBHandler("pathToDB",[basePath,'silas_labor.db']); -database = DBHandler("type",'mysql'); +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', [], ... - 'is_mpi', 1, ... - 'pam_level', 4, ... - 'wavelength', 1310, ... - 'precomp_amp', [], ... - 'signal_attenuation', [], ... - 'v_awg', [], ... - 'v_bias', [] ... - ); - +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; @@ -28,9 +27,15 @@ filterParams.Configurations = struct( ... % 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]; -selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.v_bias' 'Configurations.v_awg' 'Configurations.precomp_amp' 'Configurations.symbolrate' 'Configurations.pam_level'... - 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'Configurations.interference_path_length' 'Configurations.signal_attenuation' ... +[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'}; diff --git a/projects/ECOC_2025/theory/analytic_mpi_evaluation.m b/projects/ECOC_2025/theory/analytic_mpi_evaluation.m new file mode 100644 index 0000000..9df4f5b --- /dev/null +++ b/projects/ECOC_2025/theory/analytic_mpi_evaluation.m @@ -0,0 +1,82 @@ +% 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] +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] +tau = n_fiber./c.*L; % Interference time (= tau) [s] + +tau_c = 1/(pi*df); % laser coherence time [s] +L_c = (c/n_fiber)*tau_c; % laser coherence length [m] + +var_sat = 2*alpha^2; % Analytical saturation of variance + +%% Monte–Carlo Simulation +fs = 100e9; % sampling rate [Hz] +Tsim = 50e-6; % sim duration [s] +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 +monte_carlo_variance = zeros(num_realizations, length(L)); +parfor r = 1:num_realizations + + % generate a realization of phase noise random walk + dphi = phase_noise_std * randn(1, N + max_delay_samples); % matlab randn process has std = 1 + phi = cumsum(dphi); + phi_direct = phi(max_delay_samples+1 : max_delay_samples+N); + var_k = zeros(1, length(L)); + for t = 1:length(tau) + + nd = round( tau(t)*fs ); % delay in samples for current interference time + phi_delayed = phi(max_delay_samples+1-nd : max_delay_samples+N-nd); %cut out interfering signal part (was earlier) + + E = exp(1j*phi_direct) + alpha*exp(1j*phi_delayed); % E-fields combined + I = abs(E).^2; % photo current as magnitude square of E-field + var_k(t) = var(I); + + end + monte_carlo_variance(r, :) = var_k; +end + +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; + +%% Plot +cols = [0.3467 0.5360 0.6907 + 0.9153 0.2816 0.2878 + 0.4416 0.7490 0.4322]; + +coherence_length_multiples = 0.5:0.5:ceil(L(end)/L_c); + +figure(); +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','-'); +xticks(coherence_length_multiples.*L_c); +xticklabels(round(coherence_length_multiples.*L_c)); + +norm_to_coherence_len = 1; +if norm_to_coherence_len + xticklabels(coherence_length_multiples); + xlabel('$\tau_c$', '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); +legend('Location','southeast'); diff --git a/projects/ECOC_2025/theory/coherence_length_plot.m b/projects/ECOC_2025/theory/coherence_length_plot.m new file mode 100644 index 0000000..e6d040d --- /dev/null +++ b/projects/ECOC_2025/theory/coherence_length_plot.m @@ -0,0 +1,32 @@ +%% Parameters +df = linspace(100e3,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] + +%% 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'}) +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'); + +%% 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 diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m index 7039713..d39a24c 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m @@ -4,30 +4,39 @@ dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sio db = DBHandler("dataBase", [dataBase], "type", database_type); fp = QueryFilter(); -% fp.where('Runs', 'run_id','EQUALS', 987); +% fp.where('Runs', 'run_id','EQUALS', 987); M = 8; -fp.where('Runs', 'pam_level','EQUALS', M); -% fp.where('Runs', 'bitrate','LESS_THAN', 310e9); -fp.where('Runs', 'fiber_length','EQUALS', 2); -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', 1310); -% fp.where('Runs', 'db_mode','EQUALS', 1); -fp.where('Runs', 'rop_attenuation','EQUALS', 0); +fp.where('Runs', 'pam_level','EQUALS', M); +% fp.where('Runs', 'bitrate','LESS_THAN', 310e9); +fp.where('Runs', 'fiber_length','EQUALS', 2); +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', 'db_mode','EQUALS', 0); +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); % Create the figure -figure(10); +showFiltered = true; +showPrecoded = false; +show_bitrate = true; +figure(5); hold on -for pre_emph = [1] - - dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:); - for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse , equalizer_structure.vnle_db_mlse] +for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse] + + % figure('Name',string([char(eqs),''])); + % hold on + + for pre_emph = [0,1] + + dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:); eq_choice = equalizer_structure(eqs); @@ -37,50 +46,238 @@ for pre_emph = [1] end eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:); - - symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend'); - [~, ia] = unique(symbolrate_sorted.symbolrate, 'first'); + + % ===== NEW: compute averages + per-row keep masks (robust filtering) ===== + [Tav, keepMask, keepMaskP] = avgBerBySymbolrate(eq_filtered); % <= NEW + + % x-values (bitrate) for raw points (same mapping as your lines) + M = unique(eq_filtered.pam_level); % (assumes single PAM per curve) + if show_bitrate + x_raw = eq_filtered.symbolrate.*1e-9 .* floor(log2(M)*10)/10; + else + x_raw = eq_filtered.symbolrate.*1e-9; + end + + % ===== NEW: scatter kept raw BER points (hidden from legend) ===== + cols = cbrewer2('Paired',12); + thisColor = cols((2*eqs)+1+pre_emph,:); + scatter(x_raw(keepMask), ... % kept points + eq_filtered.BER(keepMask), ... + 14, thisColor, 'filled', ... + 'MarkerFaceAlpha', 0.35, ... + 'MarkerEdgeAlpha', 0.35, ... + 'HandleVisibility','off'); + + if showPrecoded + scatter(x_raw(keepMaskP), ... % kept precoded points + eq_filtered.BER_precoded(keepMaskP), ... + 14, thisColor, 'filled', ... + 'Marker', 'square', ... + 'MarkerFaceAlpha', 0.35, ... + 'MarkerEdgeAlpha', 0.35, ... + 'HandleVisibility','off'); + end + + % ===== NEW: optionally show filtered-out points in red ===== + if showFiltered + bad = ~keepMask; + if any(bad) + scatter(x_raw(bad), eq_filtered.BER(bad), ... + 18, 'r', 'x', 'LineWidth', 1.2, ... + 'HandleVisibility','off'); + end + badp = ~keepMaskP; + if any(badp) + scatter(x_raw(badp), eq_filtered.BER_precoded(badp), ... + 18, 'r', '+', 'LineWidth', 1.2, ... + 'HandleVisibility','off'); + end + end + + % Keep your sorting and one-per-symbolrate behavior (using Tav) + symbolrate_sorted = sortrows(Tav,{'symbolrate','avg_BER_calc'}, 'ascend'); + [~, ia] = unique(symbolrate_sorted.symbolrate, 'first'); symbolrate_sorted = symbolrate_sorted(ia, :); - - % Example data (replace these with your real vectors) - symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud - bitrate = symbolrate * floor(log2(M)*10)/10; - ber = symbolrate_sorted.min_BER; % BER - ber_precoded = symbolrate_sorted.min_BER_precoded; % BER - cols = cbrewer2('Paired',12); - cols = [0.4660 0.6740 0.1880 ; 0.9290 0.6940 0.1250 ; 0 0.4470 0.7410; 0.4940 0.1840 0.5560]; %VNLE; PF ; DFE ; DB tgt - - dname = [char(eq_choice)]; - if pre_emph + + if show_bitrate + % Bitrate for the averaged curves (unchanged) + xraw = symbolrate_sorted.symbolrate.*1e-9 .* floor(log2(M)*10)/10; + else + xraw = symbolrate_sorted.symbolrate.*1e-9; + end + % Use the MATLAB-averaged BERs + ber = symbolrate_sorted.avg_BER_calc; + ber_precoded = symbolrate_sorted.avg_BER_precoded_calc; + + + dname = strrep([char(eq_choice)],'_',' '); + if pre_emph dname = [dname,' with pre-emph.']; else dname = [dname,' w/o pre-emph.']; end - plot(bitrate, ber, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','o','LineStyle','-','Color',cols((2*eqs)+1+pre_emph,:),'MarkerEdgeColor',cols((2*eqs)+1+pre_emph,:),'MarkerFaceColor',[1,1,1],'DisplayName',[dname]); - plot(bitrate, ber_precoded, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','square','LineStyle',':','Color',cols((2*eqs)+1+pre_emph,:),'MarkerEdgeColor',cols((2*eqs)+1+pre_emph,:),'MarkerFaceColor',[1,1,1],'DisplayName',[dname,'; pre-coded']); - grid on; - - % Axis labels and title - xlabel('Baud Rate GBaud', 'FontSize', 12); - ylabel('BER', 'FontSize', 12); - title('BER vs. Baud Rate', 'FontSize', 14, 'FontWeight', 'bold'); - - % Improve tick formatting - set(gca, 'XScale', 'linear', ... - 'YScale', 'log', ... - 'TickLabelInterpreter', 'latex', ... - 'FontSize', 11); - legend + plot(xraw, ber, ... + 'LineWidth', 1.5, 'MarkerSize', 5, ... + 'Marker','o','LineStyle','-', ... + 'Color',thisColor,'MarkerEdgeColor',thisColor,'MarkerFaceColor',[1,1,1], ... + 'DisplayName', dname); - xticks(bitrate); + if showPrecoded + plot(xraw, ber_precoded, ... + 'LineWidth', 1.5, 'MarkerSize', 5, ... + 'Marker','square','LineStyle',':', ... + 'Color',thisColor,'MarkerEdgeColor',thisColor,'MarkerFaceColor',[1,1,1], ... + 'DisplayName', [dname,'; pre-coded']); + end + + grid on; + + if show_bitrate + xlabel('Net bitrate [GBps]', 'FontSize', 12); + else + xlabel('Symbol rate [GBd]', 'FontSize', 12); + end + ylabel('BER', 'FontSize', 12); + title('BER vs. Baud Rate','FontSize', 14, 'FontWeight', 'bold'); + + set(gca, 'XScale', 'linear', ... + 'YScale', 'log', ... + 'TickLabelInterpreter', 'latex', ... + 'FontSize', 11); - % Optional: tighten axis limits - xlim([min(bitrate), max(bitrate)]); - ylim([5e-5, 0.5]); + xticks(xraw); + if show_bitrate + xticks(200:25:500); + xlim([350 500]); + else + xlim([min(xraw), max(xraw)]); + end + + ylim([5e-4, 0.3]); end - + yline([2.2e-4, 4.85e-3, 2e-2],'LineWidth',1,'LineStyle','--','HandleVisibility','off'); +end + +function [Tav, keepAll, keepAllP] = avgBerBySymbolrate(T, ZT, MIN_G) +% Minimal robust averaging of BER per symbolrate (+ masks for kept points). +% Usage: [Tav, keepAll, keepAllP] = avgBerBySymbolrate(T, ZT, MIN_G) +% Defaults: ZT=3 (MAD z-thresh in log10), MIN_G=2 (min points to filter) + + if nargin < 2, ZT = 5; end + if nargin < 5, MIN_G = 0; end + + hasP = ismember('BER_precoded', T.Properties.VariableNames); + hasNB = ismember('numBits', T.Properties.VariableNames); + + [G,~,idx] = unique(T.symbolrate); + nG = numel(G); + + avgBER = nan(nG,1); + avgBERp = nan(nG,1); + keepAll = false(height(T),1); + keepAllP = false(height(T),1); + + for gi = 1:nG + r = idx==gi; + + x = T.BER(r); + nb = hasNB * T.numBits(r) + ~hasNB; % if missing, nb==1 (scalar expansion ok) + + [avgBER(gi), keepAll(r)] = rmeanBer(x, nb, ZT, MIN_G); + + if hasP + xp = T.BER_precoded(r); + [avgBERp(gi), keepAllP(r)] = rmeanBer(xp, nb, ZT, MIN_G); + end + end + + Tav = table(G, avgBER, avgBERp, ... + 'VariableNames', {'symbolrate','avg_BER_calc','avg_BER_precoded_calc'}); +end + +function [mu, keep] = rmeanBer(x, nb, ZT, MIN_G, onlyHighOutliers, minKeepThreshold) +% Robust arithmetic mean of BER with log-domain MAD filtering (returns keep mask) +% +% Params: +% x : BER values +% nb : numBits (for floor) +% ZT : MAD z-threshold +% MIN_G : min group size before filtering +% onlyHighOutliers : (bool) if true, only discard values above mean +% minKeepThreshold : values below this BER are always kept +% +% Returns: +% mu : robust mean +% keep : logical mask of kept samples + + if nargin < 5, onlyHighOutliers = false; end + if nargin < 6, minKeepThreshold = 0; end + + x(~isfinite(x)) = NaN; + + if ~isscalar(nb), nb(~isfinite(nb)) = NaN; end + if isscalar(nb) && ~isfinite(nb), nb = 1; end + + floorVal = realmin; + if ~isscalar(nb) || (isscalar(nb) && isfinite(nb) && nb~=1) + fv = 0.5 ./ max(nb, eps); % rule-of-three style floor + if isscalar(fv), floorVal = fv; else, floorVal = fv; end + end + + xAdj = x; + bad = ~isfinite(xAdj) | xAdj <= 0; + if isscalar(floorVal) + xAdj(bad) = floorVal; + else + xAdj(bad) = floorVal(bad); + end + + valid = isfinite(xAdj) & xAdj > 0; + keep = false(size(xAdj)); + + if nnz(valid)==0 + mu = NaN; return + end + if nnz(valid) < MIN_G + mu = mean(xAdj(valid),'omitnan'); keep(valid)=true; return + end + + lx = log10(xAdj(valid)); + med = median(lx,'omitnan'); + mad = median(abs(lx-med),'omitnan'); + + if mad<=0 || ~isfinite(mad) + keep(valid) = true; + mu = mean(xAdj(valid),'omitnan'); + return + end + + sigma = 1.4826*mad; + ksel = abs(lx-med) <= ZT*sigma; + + % convert to linear indices + vIdx = find(valid); + + % === Extension A: only drop high outliers === + if onlyHighOutliers + logMean = mean(lx,'omitnan'); + highIdx = lx > logMean; + ksel = ksel | ~highIdx; % always keep values below/equal to mean + end + + % === Extension B: always keep values below minKeepThreshold === + belowThr = xAdj(valid) < minKeepThreshold; + ksel = ksel | belowThr; + + keep(vIdx(ksel)) = true; + + if any(keep) + mu = mean(xAdj(keep),'omitnan'); + else + mu = mean(xAdj(valid),'omitnan'); + keep(valid) = true; + end end -yline([4.85e-3, 2e-2],'LineWidth',1,'LineStyle','--','HandleVisibility','off'); beautifyBERplot(); \ No newline at end of file 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 ad75339..2a82dc6 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 = 0; -dsp_options.max_occurences = 1; +dsp_options.append_to_db = 1; +dsp_options.max_occurences = 15; experiment = "highspeed_2024"; dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' @@ -39,29 +39,23 @@ end % === Get Run ID's === fp = QueryFilter(); -% fp.where('Runs', 'run_id','EQUALS', 987); -fp.where('Runs', 'pam_level','EQUALS', 4); -fp.where('Runs', 'bitrate','EQUALS', 360e9); -fp.where('Runs', 'fiber_length','EQUALS', 2); +% fp.where('Runs', 'run_id','EQUALS', 987); +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', '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', 1310); -fp.where('Runs', 'db_mode','EQUALS', 0); -fp.where('Runs', 'rop_attenuation','EQUALS', 0); +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); - [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); - -% Keep only the rows corresponding to the first occurrence of each 'sir' value -% [~, unique_indices] = unique(dataTable.sir, 'first'); -% dataTable = dataTable(unique_indices, :); - -% dataTable = dataTable(1,:); - % === Set LOOPS & Initialize DataStorage === dsp_options.parameters = struct(); % dsp_options.parameters.pf_ncoeffs = [1,2];%[0,logspace(-4,0,10)]; @@ -75,150 +69,281 @@ wh.addStorage("dbenc_package"); % === RUN IT === -[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true); +[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); + + % 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); + + % 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); + + 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); + + % metadata + bitrate(i) = dataTable.bitrate(i); + baudrate(i) = dataTable.symbolrate(i); +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) + +% --- 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 + +% prepare x values in GBd +xGHz = baudrate .* 1e-9; +xticks_vals = xGHz; +xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false); + +% 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}; -% wh.getStoValue('ffe_package',0.005); -% wh.getStoValue('mlse_package',0.005); +% ---------------- FIGURE : BER ---------------- +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); +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'); -% [dataTable,~] = db.queryDB(fp, [db.getTableFieldNames('Runs');db.getTableFieldNames('Results');db.getTableFieldNames('Equalizer')]); + +% ---------------- FIGURE 15 : GMI ---------------- +figure(113+M); clf; hold on; +plot(xGHz, GMI_VNLE, ... + 'DisplayName','VNLE', ... + mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(xGHz, GMI_MLSE, ... + 'DisplayName','MLSE', ... + mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +plot(xGHz, GMI_DB, ... + 'DisplayName','DB tgt.', ... + mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); + +ylim([log2(M)-1, log2(M)]); +xlabel('Baudrate in GBd'); +ylabel('GMI'); +set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); +grid on; +legend('Location','best'); + + + +% ---------------- FIGURE 15 : AIR ---------------- +m = floor(log2(M)*10)/10; +figure(114+M); clf; hold on; +plot(xGHz, GMI_VNLE.*xGHz, ... + 'DisplayName','AIR VNLE', ... + mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +% duobinary has only one GMI curve (DB output) +plot(xGHz, GMI_MLSE.*xGHz, ... + 'DisplayName','AIR MLSE', ... + mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +% MLSE symbol-wise (if present) +plot(xGHz, GMI_DB.*xGHz, ... + 'DisplayName','AIR DB tgt.', ... + mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); + +% ylim([log2(M)-1, log2(M)]); +xlabel('Baudrate in GBd'); +ylabel('AIR in Gbps'); +set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); +grid on; +legend('Location','best'); + + + + + + +% ---------------- FIGURE 15 : Information Rates ---------------- +tp = TransmissionPerformance; + + +m = floor(log2(M)*10)/10; +figure(213+M); clf; hold on; + +netrates_vnle = tp.calculateNetRate(baudrate.* m, ... + 'NGMI', GMI_VNLE./m, ... + 'BER', BER_VNLE); % -% dataTable = cleanUpTable(dataTable); -% wh_analyze = wh_adap; -% % wh_analyze = wh_dcremoval_old2; -% % wh_analyze = wh_dcremoval_old2; +% plot(xGHz, GMI_VNLE.*xGHz, ... +% 'DisplayName','GMI*R VNLE', ... +% mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); % -% res = cell(wh_analyze.parameter.mu_dc.length,wh_analyze.parameter.dc_buffer_len.length); -% ber_mean = zeros(wh_analyze.parameter.mu_dc.length,wh_analyze.parameter.dc_buffer_len.length); -% for m = 1:wh_analyze.parameter.mu_dc.length -% for b = 1:wh_analyze.parameter.dc_buffer_len.length -% res{m,b}=wh_analyze.getStoValue("ffe_package",wh_analyze.parameter.mu_dc.values(m),wh_analyze.parameter.dc_buffer_len.values(b)); -% try -% cells = res{m,b}{1}; -% idx = cellfun(@(c) ~isempty(c), cells); -% ber = cellfun(@(c) c.metrics.BER, cells(idx)); -% ber_mean(m,b) = mean(ber); -% catch -% ber_mean(m,b) = NaN; -% end -% end -% end -% -% figure() -% hold on -% ber_fix = cellfun(@(c) c.ffe_package{1}.metrics.BER, results_fix); -% ber_adap_m2 = cellfun(@(c) c.ffe_package{1}.metrics.BER, results_adap); -% ber_adap_method1 = cellfun(@(c) c.ffe_package{1}.metrics.BER, results); -% plot(dataTable.sir,ber_fix,'LineWidth',1,'DisplayName',sprintf('DCt; fix mu = 0.5; p=1024'),'Marker','.','MarkerSize',10); -% plot(dataTable.sir,ber_adap_method1,'LineWidth',1,'DisplayName',sprintf('DCt; adap mu 1; p=1024'),'Marker','.','MarkerSize',10); -% plot(dataTable.sir,ber_adap_m2,'LineWidth',1,'DisplayName',sprintf('DCt; adap mu 2; p=1024'),'Marker','.','MarkerSize',10); -% xlabel('BER'); -% xlabel('SIR'); -% yline([4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); -% % ylim([9e-4, 0.5]); -% set(gca, 'YScale', 'log'); % BER is usually plotted log-scale -% legend('show', 'Location', 'best'); -% grid on; +% plot(xGHz, netrates_vnle.SDHD.NetRate.*1e-9, ... +% 'DisplayName','SD+HD VNLE', ... +% mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +% plot(xGHz, netrates_vnle.HD.NetRate.*1e-9, ... +% 'DisplayName','Staircase VNLE', ... +% 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); % -% -% figure; clf -% -% % Create meshgrid for contourf -% [X, Y] = meshgrid(dsp_options.parameters.mu_dc, dsp_options.parameters.dc_buffer_len); -% -% % Create contour plot -% contourf(X, Y, ber_mean', 20); % 20 contour levels, adjust as needed -% -% % Set axes to logarithmic scale -% set(gca, 'XScale', 'log', 'YScale', 'log'); -% -% colormap("parula"); -% c = colorbar; -% c.Label.String = 'BER'; -% -% xlabel('\mu_{dc}'); -% ylabel('dc\_buffer\_len'); -% title('BER Optimization over \mu_{dc} and dc\_buffer\_len'); -% -% % Make plot prettier -% grid on -% set(gca, 'Layer', 'top'); % Put grid lines on top of contours -% -% -% % === Look at it === -% y_var = 'BER_precoded'; -% x_var = 'bitrate'; -% fixedVars = {'equalizer_structure', x_var}; -% -% [dataTableClean, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var); -% -% % --- Group and aggregate --- -% dataTableGrpd_mean = groupIt(fixedVars, dataTableClean, @mean); -% dataTableGrpd_min = groupIt(fixedVars, dataTableClean, @min); -% dataTableGrpd_max = groupIt(fixedVars, dataTableClean, @max); -% -% % Choose a color map -% cols = linspecer(numel(unique(dataTableGrpd_mean.equalizer_structure))); -% -% figure; -% hold on; -% -% % Get unique equalizer structures for grouping -% unique_eq = unique(dataTableGrpd_mean.equalizer_structure); -% -% for i = 1:numel(unique_eq) -% eq_val = unique_eq(i); -% -% % Filter grouped data for this equalizer structure -% filt = dataTableGrpd_mean.equalizer_structure == eq_val; -% -% x = dataTableGrpd_mean.(x_var)(filt); -% y_mean = dataTableGrpd_mean.(y_var)(filt); -% y_min = dataTableGrpd_min.(y_var)(filt); -% y_max = dataTableGrpd_max.(y_var)(filt); -% -% % Bounds for boundedline (distance from mean) -% y_lower = y_mean - y_min; -% y_upper = y_max - y_mean; -% y_bounds = [y_lower, y_upper]; -% -% % --- Bounded line (mean ± min/max) --- -% if exist('boundedline', 'file') -% [hl, hp] = boundedline(x, y_mean, y_bounds, ... -% 'alpha', 'transparency', 0.1, ... -% 'cmap', cols(i,:), ... -% 'nan', 'fill', ... -% 'orientation', 'vert'); -% set(hl, 'LineWidth', 1.2, 'DisplayName', sprintf('Eq %s', eq_val)); -% set(hp, 'HandleVisibility', 'off'); -% else -% % If boundedline is not available, use errorbar -% errorbar(x, y_mean, y_lower, y_upper, ... -% 'o-', 'Color', cols(i,:), 'LineWidth', 1.2, ... -% 'DisplayName', sprintf('Eq %d', eq_val),'HandleVisibility', 'off'); -% end -% -% % --- Normal line (mean only) --- -% plot(x, y_mean, '-', 'Color', cols(i,:), 'LineWidth', 1.5, ... -% 'DisplayName', sprintf('Mean Eq %s', eq_val),'HandleVisibility', 'off'); -% -% % --- Scatter plot for individual points (from original data) --- -% % Filter original data for this group -% orig_filt = dataTableClean.equalizer_structure == eq_val; -% x_scatter = dataTableClean.(x_var)(orig_filt); -% y_scatter = dataTableClean.(y_var)(orig_filt); -% -% scatter(x_scatter, y_scatter, 10,cols(i,:), 'filled', ... -% 'MarkerFaceAlpha', 0.5, 'DisplayName', sprintf('Scatter Eq %s', eq_val),'HandleVisibility', 'off'); -% end -% -% yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--'); -% set(gca, 'YScale', 'log'); % BER is usually plotted log-scale -% xlabel(x_var, 'Interpreter', 'none'); -% ylabel(y_var, 'Interpreter', 'none'); -% legend('show', 'Location', 'best'); -% grid on; -% title(sprintf('%s vs. %s', y_var, x_var), 'Interpreter', 'none'); -% hold off; \ No newline at end of file +% netrates_mlse = tp.calculateNetRate(baudrate.* m, ... +% '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); +% plot(xGHz, netrates_mlse.HD.NetRate.*1e-9, ... +% 'DisplayName','Staircase MLSE', ... +% mk.MLSE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.MLSE); + + +% duobinary has only one GMI curve (DB output) +figure(1111); clf; hold on; +plot(xGHz, GMI_DB.*xGHz, ... + 'DisplayName','GMI*R DB tgt.', ... + mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); + +netrates_db = tp.calculateNetRate(baudrate.* m, ... + 'NGMI', GMI_DB./m, ... + 'BER', BER_DB_PREC); + +plot(xGHz, netrates_db.SDHD.NetRate.*1e-9, ... + 'DisplayName','SD+HD DB', ... + mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB); +plot(xGHz, netrates_db.STAIR.NetRate.*1e-9, ... + 'DisplayName','Staircase DB', ... + mk.DB_precode{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.DB_precode); +plot(xGHz, netrates_db.O_FEC.NetRate.*1e-9, ... + 'DisplayName','O-FEC DB', ... + mk.DB_precode{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.DB_precode); +plot(xGHz, netrates_db.KP4_hamming.NetRate.*1e-9, ... + 'DisplayName','KP4 Hamming DB', ... + mk.DB_precode{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB_precode); + +% ylim([log2(M)-1, log2(M)]); +xlabel('Baudrate in GBd'); +ylabel('AIR in Gbps'); +set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); +grid on; +legend('Location','best'); +% xlim([1, 256]) + + + +figure(2222); clf; hold on; +plot(xGHz, GMI_MLSE.*xGHz, ... + 'DisplayName','GMI*R DB tgt.', ... + mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); + +netrates_mlse = tp.calculateNetRate(baudrate.* m, ... + 'NGMI', GMI_MLSE./m, ... + 'BER', BER_MLSE); + +plot(xGHz, netrates_mlse.SDHD.NetRate.*1e-9, ... + 'DisplayName','SD+HD DB', ... + mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +plot(xGHz, netrates_mlse.STAIR.NetRate.*1e-9, ... + 'DisplayName','Staircase DB', ... + mk.VNLE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(xGHz, netrates_mlse.O_FEC.NetRate.*1e-9, ... + 'DisplayName','O-FEC DB', ... + mk.VNLE{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(xGHz, netrates_mlse.KP4_hamming.NetRate.*1e-9, ... + 'DisplayName','KP4 Hamming DB', ... + mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE); + +% ylim([log2(M)-1, log2(M)]); +xlabel('Baudrate in GBd'); +ylabel('AIR in Gbps'); +set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); +grid on; +legend('Location','best'); +% xlim([1, 256]) + + + +figure(3333); clf; hold on; +plot(xGHz, GMI_VNLE.*xGHz, ... + 'DisplayName','GMI*R DB tgt.', ... + mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); + +netrates_vnle = tp.calculateNetRate(baudrate.* m, ... + 'NGMI', GMI_VNLE./m, ... + 'BER', BER_VNLE); + +plot(xGHz, netrates_vnle.SDHD.NetRate.*1e-9, ... + 'DisplayName','SD+HD DB', ... + mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +plot(xGHz, netrates_vnle.STAIR.NetRate.*1e-9, ... + 'DisplayName','Staircase DB', ... + mk.VNLE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(xGHz, netrates_vnle.O_FEC.NetRate.*1e-9, ... + 'DisplayName','O-FEC DB', ... + mk.VNLE{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(xGHz, netrates_vnle.KP4_hamming.NetRate.*1e-9, ... + 'DisplayName','KP4 Hamming DB', ... + mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE); + +% ylim([log2(M)-1, log2(M)]); +xlabel('Baudrate in GBd'); +ylabel('AIR in Gbps'); +set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); +grid on; +legend('Location','best'); +% xlim([1, 256]) \ 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 b040f1e..40ccbb2 100644 --- a/projects/IMDD_base_system/simulation_bwl.m +++ b/projects/IMDD_base_system/simulation_bwl.m @@ -1,7 +1,8 @@ %%% Run parameters % TX M = 6; -fsym = 112e9; +m = floor(log2(M)*10)/10; +fsym = 224e9; apply_pulsef = 1; fdac = 256e9; @@ -9,20 +10,20 @@ fadc = 256e9; random_key = 2; rcalpha = 0.05; -kover = 16; +kover = 8; vbias_rel = 0.5; -u_pi = 2.9; +u_pi = 3.2; vbias = -vbias_rel*u_pi; -laser_wavelength = 1290; -laser_linewidth = 0; +laser_wavelength = 1310; +laser_linewidth = 1e6; % Channel -link_length = 10000; +link_length = 0; vnle_order1 = 50; -vnle_order2 = 3; -vnle_order3 = 3; +vnle_order2 = 0; +vnle_order3 = 0; vnle_order=[vnle_order1,vnle_order2,vnle_order3]; dfe_order = [0 0 0]; @@ -43,20 +44,22 @@ mu_dfe = 0.0004; dfe_ = sum(dfe_order)>0; doub_mode = db_mode.no_db; - -rop = [-5]; +cols = linspecer(6); +rop = [-6]; bwl = [0.5:0.1:1.5]; -fsym = [72:8:170].*1e9; -ber_vnle = []; +fsym = [120:8:256].*1e9; +fsym =150e9; + +ber_vnle = []; ber_mlse = []; ber_viterbi = []; ber_db = []; ber_db_diff_precoded = []; -gmi_vnle = []; +gmi_vnle_bitwise = []; gmi_mlse = []; gmi_mlse_db = []; -parfor r = 1:length(fsym) +for r = 1:length(fsym) Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha); @@ -74,29 +77,94 @@ parfor r = 1:length(fsym) "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 = 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 = 50e9; - El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); + % 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 %%%%%% + %%%%% MODULATE E/O CONVERSION %%%%% + u_pi = 3.2; + 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); + %%%%%% 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); - + + % Opt_sig.eye(fsym(r),M,"fignum",103838); + + % % 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); + % 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; + %%%%%% 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 = 50e9; + 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 %%%%%% @@ -113,21 +181,29 @@ parfor r = 1:length(fsym) [~, 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 + if 0 %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); - mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); - + 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); - - mlse_.DIR = [1,1]; - [mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); - + + viterbi = 0; + 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); + 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); + end + mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(mlse_sig_sd); mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M); mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M); @@ -138,48 +214,84 @@ parfor r = 1:length(fsym) 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),~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + [~,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_mlse,ber_db(r),~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + [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); + burst_db(r,:) = count_error_bursts(a, 15)./numel(Tx_bits.signal); + + 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",[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",[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); - - [gmi_gomez(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100); - [gmi_vnle(r)] = calc_ngmi(eq_signal_sd,Symbols); - [gmi_bitwise(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',118); + 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); + % Hard decision on VNLE output eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); - [~,~,ber_vnle(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + [~,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); + fprintf('BER VNLE: %.2e \n',ber_vnle(r)); - fprintf('NGMI VNLE: %.2f \n',gmi_vnle(r)./log2(M)); + fprintf('NGMI VNLE: %.2f \n',gmi_vnle_bitwise(r)./m); + if 1 % Process through postfilter and MLSE pf_ncoeffs = 1; - pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); - mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + if fsym(r) < 200e9 + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1,"coefficients",[1,0.1]); + else + 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; 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); - [~,~,ber_mlse(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - fprintf('BER: %.2e \n',ber_mlse(r)); - fprintf('GMI MLSE: %.5f \n',gmi_mlse(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); + + showLevelConfusionMatrix(mlse_sig_hd,Symbols,"M",M,"fignum",300,"displayname",'bla'); + + fprintf('BER MLSE: %.2e \n',ber_mlse(r)); + fprintf('NGMI MLSE: %.5f \n',gmi_mlse(r)./m); + + 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); @@ -198,36 +310,166 @@ parfor r = 1:length(fsym) end -cols = cbrewer2('Paired',8); -d = 1; -figure(11);hold on -plot(fsym.*1e-9,alpha,'DisplayName','VNLE','Marker','x','LineStyle','-','Color',cols(1+d,:)); + + +% --- style control (one variable controls both marker size and linewidth) --- +STYLE_BASE = 2; % adjust this single number to scale markers & lines +MARKER_SIZE = STYLE_BASE; % marker size (MATLAB MarkerSize) +LINE_WIDTH = max(1.5, STYLE_BASE/3); % line width (keeps lines reasonable when STYLE_BASE large) + +% --- 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 + +% prepare x values in GBd +xGHz = fsym .* 1e-9; +xticks_vals = xGHz; +xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false); + +% common marker settings (filled, same face+edge color) +mk.VNLE = {'Marker','none','MarkerFaceColor',cm.MLSE,'MarkerEdgeColor',cm.VNLE,'MarkerSize',MARKER_SIZE}; +mk.MLSE = {'Marker','none','MarkerFaceColor',cm.MLSE,'MarkerEdgeColor',cm.MLSE,'MarkerSize',MARKER_SIZE}; +mk.DB_precode = {'Marker','none','MarkerFaceColor',cm.DB_precode,'MarkerEdgeColor',cm.DB_precode,'MarkerSize',MARKER_SIZE}; +mk.DB = {'Marker','none','MarkerFaceColor',cm.DB,'MarkerEdgeColor',cm.DB,'MarkerSize',MARKER_SIZE}; + +% ---------------- FIGURE 11 : alpha (VNLE) ---------------- +figure(110+M); clf; hold on; +plot(xGHz, alpha, ... + 'DisplayName','VNLE', ... + mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); xlabel('Baudrate in GBd'); ylabel('alpha'); +set(gca, 'XTick', xticks_vals, 'XTickLabel', xtick_labels); +grid on; +legend('Location','best'); -figure(15);hold on -plot(fsym.*1e-9,gmi_gomez,'DisplayName','gomez','Marker','x','LineStyle','-','Color',cols(1+d,:)); -plot(fsym.*1e-9,gmi_vnle,'DisplayName','vnle other','Marker','x','LineStyle','-','Color',cols(3+d,:)); -% plot(fsym.*1e-9,gmi_bitwise,'DisplayName','bitwise','Marker','x','LineStyle','--','Color',cols(5+d,:)); -plot(fsym.*1e-9,gmi_mlse,'DisplayName','MLSE','Marker','*'); -plot(fsym.*1e-9,gmi_mlse_db,'DisplayName','DB Output','Marker','*'); -ylim([log2(M)-1 log2(M)]); +% ---------------- FIGURE 15 : GMI ---------------- +figure(111+M); clf; hold on; +plot(xGHz, mi_gomez, ... + 'DisplayName','MI VNLE', ... + mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(xGHz, gmi_vnle_bitwise, ... + 'DisplayName','GMI VNLE', ... + mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +% duobinary has only one GMI curve (DB output) +plot(xGHz, gmi_mlse_db, ... + 'DisplayName','GMI DB tgt.', ... + mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); +% MLSE symbol-wise (if present) +plot(xGHz, gmi_mlse, ... + 'DisplayName','GMI MLSE', ... + mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); + +ylim([log2(M)-1, log2(M)]); xlabel('Baudrate in GBd'); ylabel('GMI'); +set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); +grid on; +legend('Location','best'); +% xlim([184, 256]) + +% ---------------- FIGURE 13 : BER ---------------- +figure(312+M); 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_viterbi, ... + 'DisplayName','Viterbi', ... + mk.MLSE{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.MLSE); + +yline(4.85e-3,'LineWidth',1,'HandleVisibility','off'); +yline(2.2e-4,'LineWidth',1,'HandleVisibility','off'); + +plot(xGHz, ber_db, ... + 'DisplayName','DB tgt.', ... + mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); + +plot(xGHz, ber_db_diff_precoded, ... + 'DisplayName','Prec. + DB tgt.', ... + mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB); -figure(13);hold on -plot(fsym.*1e-9,ber_vnle,'DisplayName','VNLE','Marker','x','LineStyle','-','Color',cols(1+d,:)); -plot(fsym.*1e-9,ber_mlse,'DisplayName','MLSE','Marker','x','LineStyle','-','Color',cols(3+d,:)); -plot(fsym.*1e-9,ber_viterbi,'DisplayName','Viterbi','Marker','x','LineStyle','--','Color',cols(5+d,:)); -plot(fsym.*1e-9,ber_db_diff_precoded,'DisplayName','MLSE db diff','Marker','.','MarkerSize',15,'LineStyle','-'); -plot(fsym.*1e-9,ber_db,'DisplayName','MLSE db','Marker','.','MarkerSize',15,'LineStyle','-'); xlabel('Baudrate in GBd'); ylabel('BER'); set(gca, 'yscale', 'log'); -% ylim([1e-6 0.1]); -legend +set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); +grid on; +legend('Location','best'); +% xlim([184, 256]) +% ---------------- FIGURE 15 : Information Rates ---------------- +tp = TransmissionPerformance; + + +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); +% +plot(xGHz, gmi_vnle_bitwise.*xGHz, ... + 'DisplayName','GMI*R VNLE', ... + mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); + +plot(xGHz, netrates_vnle.SDHD.NetRate.*1e-9, ... + 'DisplayName','SD+HD VNLE', ... + mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(xGHz, netrates_vnle.HD.NetRate.*1e-9, ... + 'DisplayName','Staircase VNLE', ... + 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); +plot(xGHz, netrates_mlse.SDHD.NetRate.*1e-9, ... + 'DisplayName','SD+HD MLSE', ... + mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +plot(xGHz, netrates_mlse.HD.NetRate.*1e-9, ... + 'DisplayName','Staircase MLSE', ... + mk.MLSE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.MLSE); + + +% duobinary has only one GMI curve (DB output) +plot(xGHz, gmi_mlse_db.*xGHz, ... + 'DisplayName','GMI*R DB tgt.', ... + mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); + +netrates_db = tp.calculateNetRate(fsym.* m, ... + 'NGMI', gmi_mlse_db./m, ... + 'BER', ber_db); + +plot(xGHz, netrates_db.SDHD.NetRate.*1e-9, ... + 'DisplayName','SD+HD DB', ... + mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB); +plot(xGHz, netrates_db.HD.NetRate.*1e-9, ... + 'DisplayName','Staircase DB', ... + mk.DB{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.DB); + + + +% ylim([log2(M)-1, log2(M)]); +xlabel('Baudrate in GBd'); +ylabel('AIR in Gbps'); +set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); +grid on; +legend('Location','best'); +xlim([184, 256]) % Auxiliary nested helper for numerically stable log-sum-exp function s = logsumexp(a) diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m new file mode 100644 index 0000000..347897a --- /dev/null +++ b/projects/WDM/WDM_model.m @@ -0,0 +1,268 @@ +%%% Run parameters +% TX + +M = 4; +m = floor(log2(M)*10)/10; +fsym = 224e9; + +apply_pulsef = 1; +fdac = 2*fsym; +fadc = 2*fsym; +random_key = 2; + +rcalpha = 0.05; +kover = 8; +vbias_rel = 0.5; +u_pi = 3.2; +vbias = -vbias_rel*u_pi; + +laser_linewidth = 0e6; + + +% Channel +link_length = 2; +rop = -5; + +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); + +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); + +db_precode = 0; +db_encode = 0; +duob_mode = db_mode.no_db; +apply_pulsef = 0; + + +% AWG("fdac",fdac,"f_cutoff",fsym,"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); + +wavelengthplan = calcWavelengthPlan(16,400e9,1310); +wavelengthplan = [1295,1305,1315,1325]; +N = numel(wavelengthplan); +f_plan = physconst('lightspeed')./(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; + +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; + +signal_cell = {}; +Symbols = {}; +Tx_bits = {}; + +rop = linspace(-11,0,6); %12 workers when parallel + +num_realiz = 10; +gmi_vnle_bitwise = NaN(length(wavelengthplan),length(rop),num_realiz); +snr_vnle= NaN(length(wavelengthplan),length(rop),num_realiz); +ber_vnle= NaN(length(wavelengthplan),length(rop),num_realiz); + +for realiz = 1:num_realiz + + for l = 1:N + + [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(... + "fsym",fsym,"M",M,"order",18,"useprbs",0,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.5,... + "applypulseform",apply_pulsef,"pulseformer",Pform,... + "randkey",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.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); + + %%%%% 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 %%%%% + 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); + + signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",30).process(Eml_out); + end + + Opt_sig_wdm = Optical_Multiplex("fs_in",signal_cell{l}.fs,"fs_out",upsample_pow*Eml_out.fs,... + "lambda_center",1310,"random_key",0,"filtype",1,"B",200e9).process(signal_cell); + + Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",3+10*log10(N)).process(Opt_sig_wdm); + + % Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); + + % Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',1,'max_num_lines',2); + + %%%%%% Fiber %%%%%% + Opt_sig_wdm_fib=Opt_sig_wdm; + + nSegments = 2; + 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 + + Opt_sig_wdm_fib = DP_Fiber("L",link_length/nSegments,"D",Dvec(s),"Dpmd",0.1,"Ds",0.06,... + "beat_len",10,"corr_len",100,"dz",1,"manakov",0,... + "gamma",0.0023,"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 + + Opt_sig_wdm_fib.spectrum("fignum",realiz,"displayname",'bla','lambda0_nm',1310,'useWavelengthAxis',0); + + % 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) + + parfor ri = 1:length(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_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); + + PD_cell = {}; + for l = 1:N + + %%%%%% 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.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1); + + %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% + rx_bwl = 100e9; + 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); + % Scpe_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1); + + [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 1); + Rx_sig = Scpe_cell{1}; + Rx_sig = Rx_sig.normalize("mode","rms"); + + % 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_signal_sd, eq_noise] = eq_.process(Rx_sig, Symbols{l}); + showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ'); + [mi_gomez] = calc_air(eq_signal_sd, Symbols{l}, "skip_front", 100, "skip_end", 100); + [gmi_vnle_bitwise(l,ri,realiz)] = calc_ngmi(eq_signal_sd,Symbols{l}); + % [gmi_bitwise_2] = calc_gmi_bitwise(eq_signal_sd,Symbols{l}); + snr_vnle(l,ri,realiz) = calc_snr(Symbols{l}, eq_signal_sd-Symbols{l}); + + % eq_signal_sd.plot("displayname",'bla','fignum',199); + % eq_signal_sd.eye(fsym,M,"fignum",103837); + + eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); + rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); + [~,tot_err,ber_vnle(l,ri,realiz),a] = calc_ber(rx_bits.signal,Tx_bits{l}.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + burst_vnle = count_error_bursts(a, 10)./tot_err; + + % showLevelConfusionMatrix(eq_signal_hd,Symbols{l},"M",M,"fignum",200,"displayname",'bla'); + % showLevelScatter(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','f_sym',fsym,'fignum',201); + % show2Dconstellation(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','fignum',2241); + + fprintf('CH %d :BER VNLE: %.2e \n',l,ber_vnle(l,ri,realiz)); + fprintf('CH %d :NGMI VNLE: %.2f \n',l,gmi_vnle_bitwise(l,ri,realiz)./m); + + end + + end + +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,squeeze(ber_vnle(l,:,:)),'Marker','*','DisplayName',sprintf('Ch: %d',wavelengthplan(l)),'Color',cols(l,:),'HandleVisibility','on') +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); + + +function dispersion_vector = getDispersionVector(N, D, ref_zdw, randomize_ZDW, randomkey) +% 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 +% alternating sign (+D, -D, +D, …). For D==0 it is flat (0) except for +% ZDW randomization. The ZDW detuning is ~N(0, 2 nm) around 1310 nm and is +% converted to dispersion via 0.09 ps/(nm·km) per nm. + + % constants (matching the Python code) + meanLambda_nm = 1310; % center wavelength + sigma_nm = 2; % ZDW sigma + Dslope = 0.09; % ps/(nm·km) per nm detuning + + % random ZDW-induced dispersion offset + if randomize_ZDW + rng(randomkey, 'twister'); + rand_zdws_nm = meanLambda_nm + sigma_nm .* randn(N,1); + rand_D = (rand_zdws_nm - ref_zdw) .* Dslope; % ps/(nm·km) + else + rand_D = zeros(N,1); + end + + % nominal segmented pattern (match Python intent; keep length N) + if D > 0 + base = (-1) .^ ((0:N-1).'); % +1,-1,+1,-1,... + else % D == 0 (or anything else) + base = ones(N,1); + end + + dispersion_vector = base .* D + rand_D; % ps/(nm·km) +end diff --git a/test/bcjr_pam.m b/test/bcjr_pam.m new file mode 100644 index 0000000..674b55f --- /dev/null +++ b/test/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/test/duobinary_emulation_minimal_example.m b/test/duobinary_emulation_minimal_example.m index 3f808b3..4bb75ca 100644 --- a/test/duobinary_emulation_minimal_example.m +++ b/test/duobinary_emulation_minimal_example.m @@ -1,65 +1,49 @@ -useprbs = 1; + + M = 6; -randkey = 1; -datarate = 224e9; -fsym = round(datarate / log2(M)) ; +apply_precode = 1; -db_pre = 1; +bitpattern = []; +s = RandStream('twister','Seed',1); +for i = 1:log2(M) + N = 2^(17-1); %length of prbs + bitpattern(:,i) = randi(s,[0 1], N, 1); +end -Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",0.05); +if M == 6 + bitpattern = reshape(bitpattern',[],1); + bitpattern = bitpattern(1:end-mod(length(bitpattern),5)); +end -[d,Symbols,Bits] = PAMsource(... - "fsym",fsym,"M",M,"order",17,"useprbs",1,... - "fs_out",fsym,... - "applyclipping",0,"clipfactor",1.5,... - "applypulseform",0,"pulseformer",Pform,... - "randkey",1,... - "db_precode",db_pre,"db_encode",0,... - "mrds_code",0,"mrds_blocklength",512).process(); +bits = Informationsignal(bitpattern); -%%%CHANNEL +symbols = PAMmapper(M,0).map(bits); -% s = RandStream('twister','Seed',2); -% start = 10000; -% burstwidth = 100; -% d_burst = d; -% for pos = start:start+burstwidth -% lvls = 1.5 .* PAMmapper(M,0).levels / rms(PAMmapper(M,0).levels); -% d_burst.signal(pos) = d.signal(pos)+randn(s,1,1); -% end +bits_rx = PAMmapper(M,0).demap(symbols); +[~,~,ber_direct,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); -d_resample = d.resample("fs_out",2.*fsym); +if apply_precode + symbols_tx = Duobinary().precode(symbols); +else + symbols_tx = symbols; +end -eq_ffe = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",1024,"mu_dd",0.0004,"mu_tr",0,"order",25,"sps",2,"decide",1); -d_eq = eq_ffe.process(d_resample,Symbols); +show2Dconstellation(symbols_tx,symbols_tx,"displayname",'VNLE Out','fignum',2241); + -% s = RandStream('twister','Seed',2); -% start = 10000; -% burstwidth = 100; -% d_burst = d_eq; -% for pos = start:start+burstwidth -% lvls = 1.5 .* PAMmapper(M,0).levels / rms(PAMmapper(M,0).levels); -% d_burst.signal(pos) = d_eq.signal(pos)+randn(s,1,1); -% end -% -% d_burst = PAMmapper(M,0).decide_pamlevel(d_burst); - -if db_pre +if apply_precode % Entschiedene Symbole codieren: d_DB(n) = d(n) + d(n-1) (im Fall von PAM4 7 level [0 1 2 3 4 5 6]) - d_db = Duobinary().encode(d); + symbols_db = Duobinary().encode(symbols_tx); % Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4 - d_dec = Duobinary().decode(d_db); + symbols_rx = Duobinary().decode(symbols_db); else - d_dec = d_burst; + symbols_rx = symbols_tx; end % Vergleichen von b(n) und d_dec(n) -Rx_bits = PAMmapper(M,0).demap(d_dec); - -Tx_bits = Bits; - -[~,error_num,ber,error_pos] = calc_ber(Tx_bits.signal,Rx_bits.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); +bits_rx = PAMmapper(M,0).demap(symbols_rx); +[~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1); disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]); diff --git a/test/minimal_example_bcjr.m b/test/minimal_example_bcjr.m new file mode 100644 index 0000000..a692d31 --- /dev/null +++ b/test/minimal_example_bcjr.m @@ -0,0 +1,171 @@ + +M_format = [2,4,6,8]; + +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]; % 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); + + sir = 10:25; + for s = 1:length(sir) + + % apply noise + y = awgn(y_filt,sir(s),"measured",1); + + % 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); + + BER_llr(m,s) = nnz(bits_tx ~= bits_rx) / numel(bits_tx); + fprintf('BER = %.2e \n', BER_llr); + end +end + +figure();hold on +for m = 1:length(M_format) + plot(sir,BER_llr(m,:),'DisplayName',sprintf('PAM %d',M_format(m))) + % plot(sir,BER_vit(m,:),'DisplayName',sprintf('PAM %d',M_format(m)),'LineStyle',':','LineWidth',0.1,'HandleVisibility','off'); +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(sir,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/test/pam_6_differential_code_understand.m b/test/pam_6_differential_code_understand.m new file mode 100644 index 0000000..3f13c5a --- /dev/null +++ b/test/pam_6_differential_code_understand.m @@ -0,0 +1,79 @@ +M = 6; +data = [1,2,3,4,5,6]; + +M = 6; + +bitpattern = []; +s = RandStream('twister','Seed',1); +for i = 1:log2(M) + N = 2^(12-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_tx_prec = Duobinary().precode(symbols); + +% all possible transitions (for now 36, including the "edges" +% of the QAM 32 constellation) +states = PAMmapper(6,0,"eth_style",0).levels; +pam6transitions = combvec(states,states)'; % pam6transitions = +% [-5 -5; +% -3 -5; +% -1 -5; ... +pam6transitions_serial = reshape(pam6transitions',[],1); + +data = pam6transitions_serial; +data = round(data); +b = min(data); +data = data - b; +data = data ./ 2; +% THIS WAS USED! +bk = zeros(size(data)); +for k = 2:numel(data) + bk(k) = mod(data(k)-bk(k-1),M); +end + + +%% State Analysis +x = bk;%symbols_tx_prec.signal; +levels = sort(unique(x)).'; % or provide known 1x6 level values + +[~,ix] = min(abs(x - levels),[],2); +x = levels(ix); % snapped/quantized + +%% TRANSITION COUNTS & PROBABILITIES +K = numel(levels); +% map to state indices 1..K +[tf, idx] = ismember(x, levels); +idx = idx(:); +from = idx(1:end-1); +to = idx(2:end); +from = idx(1:2:end); +to = idx(2:2:end); + +% counts C(from,to) +C = accumarray([from,to], 1, [K K], @sum, 0); +% row-stochastic transition matrix P(to|from) +rowSums = sum(C,2); +P = C ./ max(rowSums,1); + +%% 1) HEATMAP (which transitions are more probable?) +figure('Name','Transition Probabilities (to | from)'); +h = heatmap(levels, levels, P, 'Colormap', parula, 'ColorbarVisible','on'); +colormap(gca,[[1,1,1];flip(cbrewer2('Spectral',100))]);clim([0,ceil(max(P(:))*10)/10]); +h.XLabel = 'From state (level)'; +h.YLabel = 'To state (level)'; +h.Title = 'P(to | from)'; + +%% 2) WEIGHTED TRANSITION GRAPH +% Use dtmc if you have Econometrics Toolbox: +mc = dtmc(P, 'StateNames', string(levels)); +figure('Name','Markov Graph (dtmc)'); +gp = graphplot(mc, 'ColorEdges',true, 'LabelEdges',true); \ No newline at end of file diff --git a/test/pam_6_states_analysis.m b/test/pam_6_states_analysis.m new file mode 100644 index 0000000..a92d3ca --- /dev/null +++ b/test/pam_6_states_analysis.m @@ -0,0 +1,212 @@ + + +M = 6; + +bitpattern = []; +s = RandStream('twister','Seed',1); +for i = 1:log2(M) + N = 2^(17-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); + +bits_rx = PAMmapper(M,0).demap(symbols); +[~,~,ber_direct,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); +assert(ber_direct==0,'Mapping is wrong'); + +nBursts = 0; +% No Precoding %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%SEND DIRECTLY +symbols_tx = symbols; + +symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, nBursts, 42); + +%RECEIVE BRANCH (do nothing special) +bits_rx = PAMmapper(M,0).demap(symbols_rx); + +[~,~,ber,errpos] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); +disp(['BER normal: - ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]); +bursts_normal = count_error_bursts(errpos, 20); + + +% Precode Emulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%SEND DIRECTLY +symbols_tx = symbols; + +symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, nBursts, 42); + +%REFERENCE BRACH +symbols_db = Duobinary().encode(symbols_tx); +symbols_tx_emu = Duobinary().decode(symbols_db); +bits_tx_emu = PAMmapper(M,0).demap(symbols_tx_emu); + +% symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, 200, 42); + +%RECEIVE BRANCH +symbols_db = Duobinary().encode(symbols_rx); +symbols_rx_emu = Duobinary().decode(symbols_db); +bits_rx = PAMmapper(M,0).demap(symbols_rx_emu); + +[~,~,ber_precode_emulation,errpos_precode_emulation] = calc_ber(bits_tx_emu.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); +disp(['BER precode emulation: ',sprintf('%.1E',ber_precode_emulation),' - - PAM-',num2str(M)]); +bursts_precode_emulation = count_error_bursts(errpos_precode_emulation, 20); + + + + +% Precode at Tx %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%SEND PRECODED DATA +symbols_tx_prec = Duobinary().precode(symbols); + +symbols_rx_prec = introduce_symbol_errors(symbols_tx_prec, 1, 10, nBursts, 42); + +%RECEIVE BRANCH +symbols_db = Duobinary().encode(symbols_rx_prec); +symbols_rx_prec = Duobinary().decode(symbols_db); +bits_rx = PAMmapper(M,0).demap(symbols_rx_prec); + +[~,~,ber_precoded,errpos_precoded] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); +disp(['BER precoded: ',sprintf('%.1E',ber_precoded),' - - PAM-',num2str(M)]); +burst_precoded = count_error_bursts(errpos_precoded, 20); + + +% Precode at Tx but omit at Rx %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%SEND PRECODED DATA +symbols_tx_prec = Duobinary().precode(symbols); +bits_tx_prec = PAMmapper(M,0).demap(symbols_tx_prec); + +symbols_rx_omit = introduce_symbol_errors(symbols_tx_prec, 1, 10, nBursts, 42); + +%RECEIVE BRANCH +bits_rx = PAMmapper(M,0).demap(symbols_rx_omit); + +[~,~,ber_omit,errpos_omit] = calc_ber(bits_tx_prec.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); +disp(['BER (omit precode): ',sprintf('%.1E',ber_omit),' - - PAM-',num2str(M)]); +burst_omit = count_error_bursts(errpos_omit, 20); + +if 0 + cols = linspecer(8); + figure();hold on; + stem(1:20,bursts_normal,'LineWidth',2,'Color',cols(4,:),'Marker','_','DisplayName','w/o diff. precoder'); + stem(1:20,bursts_precode_emulation,'LineWidth',2,'Color',cols(3,:),'Marker','.','LineStyle','-','DisplayName','emulated precoder'); + stem(1:20,burst_precoded,'LineWidth',1,'Color',cols(6,:),'Marker','_','DisplayName','w/ diff. precoder'); + stem(1:20,burst_omit,'LineWidth',1,'Color',cols(5,:),'Marker','.','LineStyle',':','DisplayName','omit precoder'); + xlabel('Bit Error Burst Length') + ylabel('Occurence') + set(gca, 'yscale', 'log'); +end + + +%% State Analysis +signal_to_analyze = symbols_tx_emu; +x = signal_to_analyze.signal(:); +levels = sort(unique(x)).'; % or provide known 1x6 level values + +[~,ix] = min(abs(x - levels),[],2); +x = levels(ix); % snapped/quantized + +%% TRANSITION COUNTS & PROBABILITIES +K = numel(levels); +% map to state indices 1..K +[tf, idx] = ismember(x, levels); +idx = idx(:); +from = idx(1:end-1); +to = idx(2:end); +from = idx(1:2:end); +to = idx(2:2:end); + +% counts C(from,to) +C = accumarray([from,to], 1, [K K], @sum, 0); +% row-stochastic transition matrix P(to|from) +rowSums = sum(C,2); +P = C ./ max(rowSums,1); + +%% 1) HEATMAP (which transitions are more probable?) +figure('Name','Transition Probabilities (to | from)'); +h = heatmap(levels, levels, P, 'Colormap', parula, 'ColorbarVisible','on'); +colormap(gca,[[1,1,1];flip(cbrewer2('Spectral',100))]);clim([0,ceil(max(P(:))*10)/10]); +h.XLabel = 'From state (level)'; +h.YLabel = 'To state (level)'; +h.Title = 'P(to | from)'; + +%% 2) WEIGHTED TRANSITION GRAPH +% Use dtmc if you have Econometrics Toolbox: +mc = dtmc(P, 'StateNames', string(levels.*PAMmapper(M,0).get_scaling)); +figure('Name','Markov Graph (dtmc)'); +gp = graphplot(mc, 'ColorEdges',true, 'LabelEdges',true); + + +function symbols = introduce_symbol_errors(symbols, j, maxBurstLen, nBursts, seed) +%INTRODUCE_SYMBOL_ERRORS injects bursty level errors into symbols.signal. +% symbols.signal : column/row vector of quantized levels (exactly one of 6 values) +% j : max level step per sample (default 1) +% maxBurstLen : maximum burst length (default 8) +% nBursts : number of bursts to insert (default ~1% of length) +% seed : RNG seed (optional) + + if nargin < 2 || isempty(j), j = 1; end + if nargin < 3 || isempty(maxBurstLen), maxBurstLen = 8; end + x = symbols.signal(:); + N = numel(x); + if nargin < 4 || isempty(nBursts), nBursts = max(1, round(0.01*N)); end + if nargin >= 5 && ~isempty(seed), rng(seed); end + + % known levels and index mapping + lvls = sort(unique(x)).'; + K = numel(lvls); + + [~, idx] = ismember(x, lvls); % idx in 1..6 + + used = false(N,1); % avoid overlapping bursts + burst_ranges = zeros(nBursts,2); + + for b = 1:nBursts + % pick start not inside an existing burst + s = randi(N); + while used(s), s = randi(N); end + L = randi(maxBurstLen); + e = min(N, s+L-1); + + % mark used range + used(s:e) = true; + burst_ranges(b,:) = [s e]; + + % choose one direction for the whole burst: -1 (down) or +1 (up) + dir = randi([0 1])*2 - 1; + + % apply level errors within the burst + for t = s:e + k = idx(t); % current level index (1..6) + + % force inward movement at edges; prevents "flipping" to opposite edge + if k == 1 && dir == -1, dir = +1; end + if k == K && dir == +1, dir = -1; end + + step = randi([1 j]); % 1..j steps + kNew = k + dir*step; + + % clamp to [1,K], no wrap-around + if kNew < 1, kNew = 1; elseif kNew > K, kNew = K; end + + % if clamped to the same edge repeatedly, flip direction to keep changing + if kNew == k + dir = -dir; + kNew = max(1, min(K, k + dir*step)); + end + + idx(t) = kNew; + end + end + + x_err = lvls(idx); + symbols.signal = reshape(x_err, size(symbols.signal)); % preserve original shape + +end From f543194c08cf80f86b6082ce860bb2a3f101758e Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 17 Sep 2025 14:39:32 +0200 Subject: [PATCH 10/30] cluster changes WDM part 1 --- projects/WDM/WDM_model.m | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index 347897a..013113b 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -1,5 +1,12 @@ %%% Run parameters % TX +% --- FIRST LINE: evaluate settings located beside this script --- +run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m')); + +% optional: size local parpool from Slurm allocation (minimal, no arrays) +cpus = str2double(getenv('SLURM_CPUS_PER_TASK')); +if ~isfinite(cpus) || cpus < 1, cpus = max(1, feature('numcores')); end +p = gcp('nocreate'); if isempty(p), parpool('local', cpus); end M = 4; m = floor(log2(M)*10)/10; @@ -218,6 +225,42 @@ for realiz = 1:num_realiz end +res = struct(); % placeholder +res.gmi_vnle_bitwise = gmi_vnle_bitwise; % placeholder +res.snr_vnle = snr_vnle; +res.ber_vnle = ber_vnle; + +% --------------------------------------------- + +% result filename (timestamp + optional job id) +t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); +jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end +host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end + +% Output directory depends on platform +if ispc + output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\'); +else + output_root = '/work_beegfs/sutef391/results_WDM'; +end +if ~exist(output_root,'dir'), mkdir(output_root); end + +% Build filename +t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); +jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end +host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end + +fname = sprintf('WDM_%s_%s_%s.mat', char(t), host, jobid); + +% Save results +save(fullfile(output_root, fname), 'res', '-v7.3'); +fprintf('Saved results to: %s\n', fullfile(output_root, fname)); + + + + + + figure();hold on; cols = linspecer(N); for l = 1:N From e8afa72158d296fb5eccdf362fafd053c0a725f3 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 17 Sep 2025 14:39:56 +0200 Subject: [PATCH 11/30] cluster changes pt. 2 --- projects/WDM/WDM_settings.m | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 projects/WDM/WDM_settings.m diff --git a/projects/WDM/WDM_settings.m b/projects/WDM/WDM_settings.m new file mode 100644 index 0000000..ce45432 --- /dev/null +++ b/projects/WDM/WDM_settings.m @@ -0,0 +1,10 @@ +% Add the imdd_simulation framework to the path +if ispc + addpath(genpath('C:\Users\Silas\Documents\MATLAB\imdd_simulation')); +else + % Linux path on the cluster + addpath(genpath('/work_beegfs/sutef391/imdd_simulation')); +end + +% Define where results should be written (used by WDM_model.m) +output_root = '/work_beegfs/sutef391/results_WDM'; \ No newline at end of file From cbfdf4d222e34853da5323148534e9868c88226b Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 17 Sep 2025 15:16:55 +0200 Subject: [PATCH 12/30] wdm update pt 3 --- projects/WDM/WDM_model.m | 83 +++++++++++++------------------------ projects/WDM/WDM_settings.m | 28 ++++++++++++- 2 files changed, 54 insertions(+), 57 deletions(-) diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index 013113b..14db6dd 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -3,58 +3,39 @@ % --- FIRST LINE: evaluate settings located beside this script --- run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m')); -% optional: size local parpool from Slurm allocation (minimal, no arrays) -cpus = str2double(getenv('SLURM_CPUS_PER_TASK')); -if ~isfinite(cpus) || cpus < 1, cpus = max(1, feature('numcores')); end -p = gcp('nocreate'); if isempty(p), parpool('local', cpus); end - M = 4; m = floor(log2(M)*10)/10; fsym = 224e9; - -apply_pulsef = 1; fdac = 2*fsym; fadc = 2*fsym; +link_length = 0; +wavelengthplan = calcWavelengthPlan(16,400e9,1310); +wavelengthplan = [1295,1305,1315,1325]; random_key = 2; -rcalpha = 0.05; -kover = 8; +% Laser / Modulator vbias_rel = 0.5; u_pi = 3.2; vbias = -vbias_rel*u_pi; - laser_linewidth = 0e6; - -% Channel -link_length = 2; -rop = -5; - +% EQ SETTINGS 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); +rcalpha = 0.05; Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); db_precode = 0; @@ -63,20 +44,12 @@ duob_mode = db_mode.no_db; apply_pulsef = 0; -% AWG("fdac",fdac,"f_cutoff",fsym,"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); - -wavelengthplan = calcWavelengthPlan(16,400e9,1310); -wavelengthplan = [1295,1305,1315,1325]; N = numel(wavelengthplan); f_plan = physconst('lightspeed')./(wavelengthplan.*1e-9); margin = 25e12; % some THz left and right f_span = (max(f_plan)+margin)-(min(f_plan)-margin); f_nyq = f_span/2; - +kover = 8; upsample_required = f_nyq./(fdac*kover/2); upsample_pow = 2^nextpow2(upsample_required); upsample_ceil = ceil(upsample_required); @@ -88,9 +61,9 @@ signal_cell = {}; Symbols = {}; Tx_bits = {}; -rop = linspace(-11,0,6); %12 workers when parallel +rop = linspace(-11,0,8); -num_realiz = 10; +num_realiz = 1; gmi_vnle_bitwise = NaN(length(wavelengthplan),length(rop),num_realiz); snr_vnle= NaN(length(wavelengthplan),length(rop),num_realiz); ber_vnle= NaN(length(wavelengthplan),length(rop),num_realiz); @@ -224,6 +197,21 @@ for realiz = 1:num_realiz 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,squeeze(ber_vnle(l,:,:)),'Marker','*','DisplayName',sprintf('Ch: %d',wavelengthplan(l)),'Color',cols(l,:),'HandleVisibility','on') +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); + res = struct(); % placeholder res.gmi_vnle_bitwise = gmi_vnle_bitwise; % placeholder @@ -256,25 +244,10 @@ fname = sprintf('WDM_%s_%s_%s.mat', char(t), host, jobid); save(fullfile(output_root, fname), 'res', '-v7.3'); fprintf('Saved results to: %s\n', fullfile(output_root, fname)); - - - - - -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,squeeze(ber_vnle(l,:,:)),'Marker','*','DisplayName',sprintf('Ch: %d',wavelengthplan(l)),'Color',cols(l,:),'HandleVisibility','on') -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); +% --- 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) diff --git a/projects/WDM/WDM_settings.m b/projects/WDM/WDM_settings.m index ce45432..49ddaf4 100644 --- a/projects/WDM/WDM_settings.m +++ b/projects/WDM/WDM_settings.m @@ -6,5 +6,29 @@ else addpath(genpath('/work_beegfs/sutef391/imdd_simulation')); end -% Define where results should be written (used by WDM_model.m) -output_root = '/work_beegfs/sutef391/results_WDM'; \ No newline at end of file +% Quiet the ambiguous CET warning (best is to set TZ in sbatch; see below) +warning('off','MATLAB:datetime:AmbiguousTimeZone'); + +% How many workers? +cpus = str2double(getenv('SLURM_CPUS_PER_TASK')); +if ~isfinite(cpus) || cpus < 1, cpus = max(1, feature('numcores')); end + +% Use a per-job, node-local JobStorageLocation to avoid stale locks on $HOME +% Prefer $TMPDIR if your cluster provides it, else tempdir(). +tmpbase = getenv('TMPDIR'); +if isempty(tmpbase), tmpbase = tempdir; end +jsl = fullfile(tmpbase, sprintf('matlab_jobstorage_%s_%s', ... + getenv('USER'), getenv('SLURM_JOB_ID'))); +if ~exist(jsl,'dir'); mkdir(jsl); end + +% Configure the local cluster explicitly and start the pool +c = parcluster('local'); +c.NumWorkers = cpus; +c.JobStorageLocation = jsl; + +p = gcp('nocreate'); +if isempty(p) || p.NumWorkers ~= cpus + if ~isempty(p), delete(p); end + p = parpool(c, cpus); % avoids the “queued” state +end +fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation); \ No newline at end of file From 20b6d9c75a5cbc68ed47900c48f348adde6b81da Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 17 Sep 2025 15:38:58 +0200 Subject: [PATCH 13/30] add dp_fiber lib folder to the framework --- Classes/02_optical/dp_fiber_lib/CNLSE.m | 135 ++++++++++++++++++ Classes/02_optical/dp_fiber_lib/CNLSE_plain.m | 120 ++++++++++++++++ .../02_optical/dp_fiber_lib/getNLstepsize.m | 25 ++++ .../dp_fiber_lib/getNLstepsize_original.m | 27 ++++ Classes/02_optical/dp_fiber_lib/lin_step.m | 125 ++++++++++++++++ .../dp_fiber_lib/lin_step_original.m | 106 ++++++++++++++ Classes/02_optical/dp_fiber_lib/nl_step.m | 39 +++++ .../dp_fiber_lib/nl_step_original.m | 30 ++++ .../02_optical/dp_fiber_lib/split_step_loop.m | 93 ++++++++++++ projects/WDM/WDM_model.m | 77 ++++++---- projects/WDM/WDM_settings.m | 21 +++ 11 files changed, 768 insertions(+), 30 deletions(-) create mode 100644 Classes/02_optical/dp_fiber_lib/CNLSE.m create mode 100644 Classes/02_optical/dp_fiber_lib/CNLSE_plain.m create mode 100644 Classes/02_optical/dp_fiber_lib/getNLstepsize.m create mode 100644 Classes/02_optical/dp_fiber_lib/getNLstepsize_original.m create mode 100644 Classes/02_optical/dp_fiber_lib/lin_step.m create mode 100644 Classes/02_optical/dp_fiber_lib/lin_step_original.m create mode 100644 Classes/02_optical/dp_fiber_lib/nl_step.m create mode 100644 Classes/02_optical/dp_fiber_lib/nl_step_original.m create mode 100644 Classes/02_optical/dp_fiber_lib/split_step_loop.m diff --git a/Classes/02_optical/dp_fiber_lib/CNLSE.m b/Classes/02_optical/dp_fiber_lib/CNLSE.m new file mode 100644 index 0000000..d2773dc --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/CNLSE.m @@ -0,0 +1,135 @@ + +function [opt_out_struct,state] = CNLSE(opt_in_struct,state) + + % init transfer functions h.X and h.Y + h = struct('X',0,'Y',0); + state.common_beta=struct('X',0,'Y',0); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % pre calculations + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + % calculate transfer function and rotate coordines for both + % polarizations + + for n=1:2 + % get current polarization name and contrary one + curPol = state.polNames{n}; + + % extend linear transfer function depending on beta values for the + % current polarization + for n_beta = 1:length(state.beta.(curPol)) +% h.(curPol) = h.(curPol) - 1j*state.beta.(curPol)(n_beta)*(state.omega).^(n_beta-1)/factorial(n_beta-1); +% if n_beta ~= 2 + state.common_beta.(curPol) = state.common_beta.(curPol) + state.beta.(curPol)(n_beta) * (state.omega).^(n_beta-1) / factorial(n_beta-1); +% end + end + + opt_out_struct.(curPol)=opt_in_struct.(curPol).envelope; + end + + state.h=h; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Splitstep method + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +% state.SS_dzs = zeros(1,state.max_nonlin_its); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Split Step Method + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + % get nonlinear step size + [state.dz] = getNLstepsize(state,opt_out_struct); + + state.n_step = 0; + state.z_prop = 0; + state.test_dz = []; + state.powers = []; + + while state.z_prop < state.L + + + + + if state.z_prop + state.dz > state.L + state.dz = state.L - state.z_prop; + end + + + % lin conv + % opt_out_struct = [ opt_out_struct 0 0 0 0 0 ]; + % opt_out_struct + %%%%%%%%%%%%% + % STEP + % update step number + + state.n_step=state.n_step+1; + state.dzs(state.n_step)=state.dz; + + % half linear step + [opt_out_struct,state] = lin_step(state,opt_out_struct,state.dz/2); + + + % complete nonlinear step + [opt_out_struct,state] = nl_step(state,opt_out_struct,state.dz); + + % half linear step + [opt_out_struct,state] = lin_step(state,opt_out_struct,state.dz/2); + + %%%%%%%%%%%%% + % prepare next STEP + + % overlap(n_step+1,:) = opt_out_struct(M+1:end); + % opt_out_struct = opt_out_struct(1:M); + + + % get nonlinear step size + [state.dz] = getNLstepsize(state,opt_out_struct); + + end + + +% figure(88);clf;subplot(2,1,1);stem(state.test_plates);subplot(2,1,1); hold all;stem(-1000*state.test_plate_numbers);subplot(2,1,2);stem(state.dzs) + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Post Calculations + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% opt_out_struct.X.envelope = ( cos(state.psi)*cos(state.chi) + 1j*sin(state.psi)*sin(state.chi))*opt_out_struct.X + ... +% (-sin(state.psi)*cos(state.chi) - 1j*cos(state.psi)*sin(state.chi))*opt_out_struct.Y; +% + buffer.X.envelope = opt_out_struct.X; + buffer.X.type = opt_in_struct.X.type; + buffer.X.wavelength = opt_in_struct.X.wavelength; + if isfield(buffer.X,'Nase') + buffer.X.Nase = opt_in_struct.X.Nase; + else + buffer.X.Nase = 0; + end + + opt_out_struct.X =[]; + opt_out_struct.X.envelope = buffer.X.envelope; + opt_out_struct.X.type = buffer.X.type; + opt_out_struct.X.wavelength = buffer.X.wavelength; + opt_out_struct.X.Nase = buffer.X.Nase; + +% +% opt_out_struct.Y.envelope = ( sin(state.psi)*cos(state.chi) - 1j*cos(state.psi)*sin(state.chi))*opt_out_struct.X.envelope + ... +% ( cos(state.psi)*cos(state.chi) - 1j*sin(state.psi)*sin(state.chi))*opt_out_struct.Y; +% + buffer.Y.envelope = opt_out_struct.Y; + buffer.Y.type = opt_in_struct.Y.type; + buffer.Y.wavelength = opt_in_struct.Y.wavelength; + if isfield(buffer.Y,'Nase') + buffer.Y.Nase = opt_in_struct.Y.Nase; + else + buffer.Y.Nase = 0; + end + + opt_out_struct.Y =[]; + opt_out_struct.Y.envelope = buffer.Y.envelope; + opt_out_struct.Y.type = buffer.Y.type; + opt_out_struct.Y.wavelength = buffer.Y.wavelength; + opt_out_struct.Y.Nase = buffer.Y.Nase; + +% figure(100+loop);plot([real(opt_out_struct.X.envelope);real(opt_out_struct.Y.envelope)].'); +end \ No newline at end of file diff --git a/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m b/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m new file mode 100644 index 0000000..cfa7294 --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/CNLSE_plain.m @@ -0,0 +1,120 @@ + +function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state) + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % pre calculations + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + state.common_beta=struct('X',0,'Y',0); + + for n=1:2 + % get current polarization name and contrary one + curPol = state.polNames{n}; + + % extend linear transfer function depending on beta values for the current polarization + % Was ist der Sinn dieser komischen beta notation? zB. state.beta.X = [0.3142 0 -9.1105e-28 5.1068e-41] + for n_beta = 1:length(state.beta.(curPol)) + state.common_beta.(curPol) = state.common_beta.(curPol) + state.beta.(curPol)(n_beta) * (state.omega).^(n_beta-1) / factorial(n_beta-1); + end + + %opt_out_struct.(curPol)=opt_in_struct.(curPol).envelope; + end + + beta_const = state.beta.('X')(1); + beta_1 = state.beta.('X')(2); + beta_2 = state.beta.('X')(3); + beta_3 = state.beta.('X')(4); + deltaomega = state.omega; + beta_x = beta_const + beta_1 * deltaomega + 1/2 * beta_2 * deltaomega.^2 + 1/6 *beta_3 * deltaomega.^3; + +% opt_in_x = gpuArray(opt_in_x); +% opt_in_y = gpuArray(opt_in_y); + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Split Step Method + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% [opt_out_x,opt_out_y] = split_step_loop(state.L,opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin,... +% state.lin_z_test,state.corr_length,state.n_plates_done,state.missing_dz,state.brf,state.common_beta,.... +% state.chi,state.manakov,state.beat_len); + +% [opt_out_x,opt_out_y] = split_step_loop_mex(state.L,opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin,... +% state.lin_z_test,state.corr_length,state.n_plates_done,state.missing_dz,state.brf,state.common_beta,.... +% state.chi,state.manakov,state.beat_len); + + % get nonlinear step size + [state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin); + %[state.dz] = getNLstepsize_original(state,opt_out_struct); + + state.n_step = 0; + state.z_prop = 0; + state.test_dz = []; + state.powers = []; + + tic + + while state.z_prop < state.L + + % reduce step length (dz) if we are to overshoot the fiber length + % (L) in the next step + if state.z_prop + state.dz > state.L + state.dz = state.L - state.z_prop; + end + + % update step number (n) + state.n_step=state.n_step+1; + + % append current step length to logbook (dzs) + state.dzs(state.n_step)=state.dz; + + + % half linear step + [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,... + state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,... + state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,... + state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X]... + = lin_step(... + opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,... + state.dz/2,state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,... + state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,... + state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X); + + % complete nonlinear step + + [opt_in_x,opt_in_y] = nl_step(opt_in_x,opt_in_y, state.dz, state.gamma, state.chi, state.manakov, state.beat_len ,state.alpha_lin.X, state.alpha_lin.Y); + + % half linear step + [opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,... + state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,... + state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,... + state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X]... + = lin_step... + (opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,... + state.dz/2,state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,... + state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,... + state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X); + + % get nonlinear step size + [state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin); + %[state.dz] = getNLstepsize_original(state,opt_out_struct); + + + + end + + toc + + + + + + opt_out_x = (opt_in_x); + opt_out_y = (opt_in_y); + +% opt_out_x = gather(opt_in_x); +% opt_out_y = gather(opt_in_y); + +end \ No newline at end of file diff --git a/Classes/02_optical/dp_fiber_lib/getNLstepsize.m b/Classes/02_optical/dp_fiber_lib/getNLstepsize.m new file mode 100644 index 0000000..a70edb9 --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/getNLstepsize.m @@ -0,0 +1,25 @@ + +function [rDZ] = getNLstepsize(ux,uy,gamma,dzmin,dzmax,dphimax,alpha_lin) + + + maxPow = max(gamma.*max(real(ux).^2+imag(ux).^2+real(uy).^2+imag(uy).^2)); + + Leff = dphimax/maxPow; + alpha_lin = max([alpha_lin.X alpha_lin.Y]); + nl_att_len_ratio = alpha_lin*Leff; + + if nl_att_len_ratio >= 1 + rDZ = dzmax; + else + if alpha_lin == 0 + step = Leff; + else + %effective length? + step = -1/alpha_lin*log(1-nl_att_len_ratio); + end + + rDZ = min([step dzmax]); + rDZ = max([rDZ dzmin]); + end + +end \ No newline at end of file diff --git a/Classes/02_optical/dp_fiber_lib/getNLstepsize_original.m b/Classes/02_optical/dp_fiber_lib/getNLstepsize_original.m new file mode 100644 index 0000000..93a3d63 --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/getNLstepsize_original.m @@ -0,0 +1,27 @@ + +function [rDZ] = getNLstepsize_original(state,aOpt) + + ux = aOpt.X; + uy = aOpt.Y; + + maxPow = max(state.gamma.*max(real(ux).^2+imag(ux).^2+real(uy).^2+imag(uy).^2)); + + Leff = state.SS_dphimax/maxPow; + alpha_lin = max([state.alpha_lin.X state.alpha_lin.Y]); + nl_att_len_ratio = alpha_lin*Leff; + + if nl_att_len_ratio >= 1 + rDZ = state.SS_dzmax; + else + if alpha_lin == 0 + step = Leff; + else + %effective length? + step = -1/alpha_lin*log(1-nl_att_len_ratio); + end + + rDZ = min([step state.SS_dzmax]); + rDZ = max([rDZ state.SS_dzmin]); + end + +end \ No newline at end of file diff --git a/Classes/02_optical/dp_fiber_lib/lin_step.m b/Classes/02_optical/dp_fiber_lib/lin_step.m new file mode 100644 index 0000000..eea8a1e --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/lin_step.m @@ -0,0 +1,125 @@ + +%function [rOpt,state] = lin_step(state,aOpt,aStepSize) + +function [rOpt_x,rOpt_y,z_prop,lin_z_test,... + corr_length,n_plates_done,missing_dz,n_step,test_plates,... + test_plate_numbers,brf,common_beta_x,common_beta_y,alpha_lin_x,alpha_lin_y]... + = lin_step(... + opt_x,opt_y,z_prop,lin_z_test,aStepSize,corr_length,... + n_plates_done,missing_dz,n_step,test_plates,test_plate_numbers,... + brf,common_beta_x,common_beta_y,alpha_lin_x,alpha_lin_y) + +%%%%%% 1) Update and Check Distances etc. %%%%%% + +% update propgated distance z_prop +z_prop = z_prop + aStepSize; + +% calculate the number of plates needed for the so far propagated fiber length +n_plates = ceil(z_prop/corr_length); + +% subtract the number of plates which were already processed +n_plates_left = n_plates - n_plates_done; + +% compute last plate size ( if it fits, it should be 0) +if missing_dz > aStepSize + + last_plate = aStepSize; + missing_dz = missing_dz-aStepSize; + plate_sizes = last_plate; + plate_numbers = n_plates; + +else + + last_plate = aStepSize - missing_dz - (n_plates_left-1)*corr_length; + + if missing_dz == 0 + missing_dz = []; + end + + %build vector of plate lengths with missing plate part from prev. + %iterartion , then some normal plates and finally a fraction of a plate + %to fit into the step length + plate_sizes = [missing_dz corr_length*ones(1,n_plates_left-1) last_plate]; + + if n_plates_done == 0 + plate_numbers =[(n_plates_done+1):(n_plates-1) n_plates]; + else + plate_numbers = [n_plates_done (n_plates_done+1):(n_plates-1) n_plates]; % not wrking yet + end + + %remember for next step + missing_dz = corr_length - last_plate; + +end + +plate_steps = repmat(n_step,1,length(plate_sizes)); + +% +%figure;stem(plate_sizes); +test_plates = [test_plates,plate_sizes]; + +test_plate_numbers = [test_plate_numbers, plate_numbers]; + +%%%%%% 2) Apply Waveplate Model %%%%%% + +% transfer optical envelope to frequency domain for effective convolution with transfer function h +opt_x=fft(opt_x); +opt_y=fft(opt_y); + +% db1 = gpuArray(brf.db1); +% db0 = gpuArray(brf.db0); +% common_beta_x = gpuArray(common_beta_x); +% common_beta_y = gpuArray(common_beta_y); + +db1 = (brf.db1); +db0 = (brf.db0); +common_beta_x = (common_beta_x); +common_beta_y = (common_beta_y); + +% process every waveplate with given sizes in plate_sizes +for n=1:length(plate_sizes) + dz = plate_sizes(n); + + % figure(87);subplot(2,1,1);plot(real(x(900:1150)));subplot(2,1,2);plot(real(y(900:1150))); + % MOV1=[MOV1 getframe(87)]; + + % extract rotation matrix from pre calculated matrices + matR = brf.matR{plate_numbers(n)}; + + % transform to eigenvalue of of fiber segment + tOpt.X = conj(matR(1,1))*opt_x + conj(matR(2,1))*opt_y; + tOpt.Y = conj(matR(1,2))*opt_x + conj(matR(2,2))*opt_y; + + % calculate statistical delta beta for pmd + delta_beta = 0.5*(db1+db0(n))/corr_length; + % build transfer function with delta beta + %common.beta = beta1+beta2*omega^2 + + %accumulate delta beta for log... + brf.simdgd = brf.simdgd + (db1(length(db1)/2+1)+db0(n))/corr_length; + + h.X = exp(-1j*(common_beta_x-delta_beta)*dz); + h.Y = exp(-1j*(common_beta_y+delta_beta)*dz); + % delta_beta has to be added to the transfer function + + % process with transfer function + tOpt.X = h.X.*tOpt.X ; + tOpt.Y = h.Y.*tOpt.Y ; + + % rotate back + opt_x = matR(1,1)*tOpt.X + matR(1,2)*tOpt.Y; + opt_y = matR(2,1)*tOpt.X + matR(2,2)*tOpt.Y; + +end + +lin_z_test = lin_z_test + sum(plate_sizes,2); + +%update the number of processed plates so far +n_plates_done = n_plates_done + n_plates_left; + +% attanuate the signal each linear state with alpha +% ( 0.2dB = 4.6052e-05 ) +rOpt_x=ifft(exp(-alpha_lin_x*aStepSize/2).*opt_x); % /2 not sure why (have to find it in formulas) +rOpt_y=ifft(exp(-alpha_lin_y*aStepSize/2).*opt_y); % but not relevant for now + +end \ No newline at end of file diff --git a/Classes/02_optical/dp_fiber_lib/lin_step_original.m b/Classes/02_optical/dp_fiber_lib/lin_step_original.m new file mode 100644 index 0000000..dac5b32 --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/lin_step_original.m @@ -0,0 +1,106 @@ + +function [rOpt,state] = lin_step_original(state,aOpt,aStepSize) + + + % update propgated distance z_prop + state.z_prop = state.z_prop + aStepSize; + +% if state.synchronous_plates % not waveplate model (just rotation with dz) +% state.plate_sizes = aStepSize; +% +% else + % calculate the number of plates needed for the so far propagated fiber + % length + state.n_plates = ceil(state.z_prop/state.corr_length); + + % subtract the number of plates which were already be processed + state.n_plates_left = state.n_plates - state.n_plates_done; + + % compute last plate size ( if it fits, it should be 0) + if state.missing_dz > aStepSize + + state.last_plate = aStepSize; + state.missing_dz = state.missing_dz-aStepSize; + state.plate_sizes = state.last_plate; + state.plate_numbers = state.n_plates; + + else + + state.last_plate = aStepSize - state.missing_dz - (state.n_plates_left-1)*state.corr_length; + + if state.missing_dz == 0 + state.missing_dz = []; + end + + state.plate_sizes = [state.missing_dz state.corr_length*ones(1,state.n_plates_left-1) state.last_plate]; + + if state.n_plates_done == 0 + state.plate_numbers =[(state.n_plates_done+1):(state.n_plates-1) state.n_plates]; + else + state.plate_numbers = [state.n_plates_done (state.n_plates_done+1):(state.n_plates-1) state.n_plates]; % not wrking yet + end + + state.missing_dz = state.corr_length - state.last_plate; + end + + state.plate_steps = repmat(state.n_step,1,length(state.plate_sizes)); + + % figure;stem(state.plate_sizes); + state.test_plates = [state.test_plates,state.plate_sizes]; + state.test_plate_numbers = [state.test_plate_numbers, state.plate_numbers]; +% end + + % transfer optical envelope to frequency domain for effective + % convolution with transfer function h + aOpt.X=fft(aOpt.X); + aOpt.Y=fft(aOpt.Y); + + % process every waveplate with given sizes in state.plate_sizes + for n=1:length(state.plate_sizes) + dz = state.plate_sizes(n); + +% figure(87);subplot(2,1,1);plot(real(x(900:1150)));subplot(2,1,2);plot(real(y(900:1150))); +% state.MOV1=[state.MOV1 getframe(87)]; + + % extract rotation matrix from pre calculated matrices + matR = state.brf.matR{state.plate_numbers(n)}; + + % transform to eigenvalue of of fiber segment + tOpt.X = conj(matR(1,1))*aOpt.X + conj(matR(2,1))*aOpt.Y; + tOpt.Y = conj(matR(1,2))*aOpt.X + conj(matR(2,2))*aOpt.Y; + + % calculate statistical delta beta for pmd + delta_beta = 0.5*(state.brf.db1+state.brf.db0(n))/state.corr_length; + % db1 = sqrt(3*pi/8)*(para.dgd/para.fa)/state.wave_plates.*state.omega; +% delta_beta = 0.5*(state.brf.db0(n))/state.corr_length; + + % build transfer function with delta beta + % common.beta = beta1+beta2*omega^2 + % delta beta + h.X = exp(-1j*(state.common_beta.X-delta_beta)*dz); + h.Y = exp(-1j*(state.common_beta.Y+delta_beta)*dz); + % delta_beta has to be added to the transfer function + + % process with transfer function + tOpt.X = h.X.*tOpt.X ; + tOpt.Y = h.Y.*tOpt.Y ; + + % rotate back + aOpt.X = matR(1,1)*tOpt.X + matR(1,2)*tOpt.Y; + aOpt.Y = matR(2,1)*tOpt.X + matR(2,2)*tOpt.Y; + + end + + state.lin_z_test = state.lin_z_test + sum(state.plate_sizes,2); + + %update the number of processed plates so far + state.n_plates_done = state.n_plates_done + state.n_plates_left; + + + % attanuate the signal each linear state with alpha + % ( 0.2dB = 4.6052e-05 ) + rOpt.X=ifft(exp(-state.alpha_lin.X*aStepSize/2).*aOpt.X); % /2 not sure why (have to find it in formulas) + rOpt.Y=ifft(exp(-state.alpha_lin.Y*aStepSize/2).*aOpt.Y); % but not relevant for now + + +end \ No newline at end of file diff --git a/Classes/02_optical/dp_fiber_lib/nl_step.m b/Classes/02_optical/dp_fiber_lib/nl_step.m new file mode 100644 index 0000000..800c502 --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/nl_step.m @@ -0,0 +1,39 @@ + +%function [rOpt,state] = nl_step(state,aOpt,aDz) + +function [rOpt_x,rOpt_y] = nl_step(opt_x,opt_y, dz, gamma, chi, use_manakov, beatlength, alpha_lin_x, alpha_lin_y) + + if ~use_manakov % CNLSE + + rOpt_x = opt_x .* exp( (-1j*(1/3)*gamma*dz).* ... + ( (2 + cos(2*chi)^2)*(abs(opt_x).^2) + ... + (2+2*sin(2*chi)^2)*(abs(opt_y).^2) ) ); + + rOpt_y = opt_y .* exp( (-1j*(1/3)*gamma*dz).* ... + ( (2 + cos(2*chi)^2)*(abs(opt_y).^2) + ... + (2+2*sin(2*chi)^2)*(abs(opt_x).^2) ) ); + +% A_x = opt_x; +% A_y = opt_y; +% +% rOpt_x = 1i* gamma * (abs(A_x).^2 + (2/3 .* abs(A_y).^2) ) .* A_x + ((1i * gamma / 3) * conj(A_x).*(A_y.^2) * exp(-2i * dz * 2*pi / beatlength )); +% rOpt_y = 1i* gamma * (abs(A_y).^2 + (2/3 .* abs(A_x).^2) ) .* A_y + ((1i * gamma / 3) * conj(A_y).*(A_x.^2) * exp(-2i * dz * 2*pi / beatlength )); + + + else + % estimate effective length of dz (ref?) + if (alpha_lin_x == 0) && (alpha_lin_y == 0) + Leff = dz; + else + Leff = (1-exp(-alpha_lin_x*dz))/alpha_lin_x; + end + + %compute power + power = real(opt_x).^2+imag(opt_x).^2+real(opt_y).^2+imag(opt_y).^2; +% power= abs(opt_x).^2+abs(opt_y).^2; +% powers = [powers;power]; + Hnl = exp( -1j*8/9*gamma*power*Leff); + rOpt_x = opt_x .* Hnl; + rOpt_y = opt_y .* Hnl; + end +end diff --git a/Classes/02_optical/dp_fiber_lib/nl_step_original.m b/Classes/02_optical/dp_fiber_lib/nl_step_original.m new file mode 100644 index 0000000..4fb9593 --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/nl_step_original.m @@ -0,0 +1,30 @@ + +function [rOpt,state] = nl_step(state,aOpt,aDz) + + if ~state.manakov % CNLSE + + rOpt.X = aOpt.X .* exp( (-1j*(1/3)*state.gamma*aDz).* ... + ( (2 + cos(2*state.chi)^2)*(abs(aOpt.X).^2) + ... + (2+2*sin(2*state.chi)^2)*(abs(aOpt.Y).^2) ) ); + + rOpt.Y = aOpt.Y .* exp( (-1j*(1/3)*state.gamma*aDz).* ... + ( (2 + cos(2*state.chi)^2)*(abs(aOpt.Y).^2) + ... + (2+2*sin(2*state.chi)^2)*(abs(aOpt.X).^2) ) ); + + else + % estimate effective length of dz (ref?) + if (state.alpha_lin.X == 0) && (state.alpha_lin.Y == 0) + Leff = aDz; + else + Leff = (1-exp(-state.alpha_lin.X*aDz))/state.alpha_lin.X; + end + + %compute power + power = real(aOpt.X).^2+imag(aOpt.X).^2+real(aOpt.Y).^2+imag(aOpt.Y).^2; +% power= abs(aOpt.X).^2+abs(aOpt.Y).^2; +% state.powers = [state.powers;power]; + Hnl = exp( -1j*8/9*state.gamma*power*Leff); + rOpt.X = aOpt.X .* Hnl; + rOpt.Y = aOpt.Y .* Hnl; + end +end diff --git a/Classes/02_optical/dp_fiber_lib/split_step_loop.m b/Classes/02_optical/dp_fiber_lib/split_step_loop.m new file mode 100644 index 0000000..e74f5bf --- /dev/null +++ b/Classes/02_optical/dp_fiber_lib/split_step_loop.m @@ -0,0 +1,93 @@ +function [opt_x,opt_y] = split_step_loop(L,opt_x,opt_y,gamma,SS_dzmin,SS_dzmax,SS_dphimax,alpha_lin,... + lin_z_test,corr_length,n_plates_done,missing_dz,brf,common_beta,... + chi,manakov,beat_len) + +%SPLIT_STEP_LOOP Summary of this function goes here +% Detailed explanation goes here + %Optical Input +% opt_x; +% opt_y; +% +% %required for loop condition +% z_prop = 0; +% L; +% +% %required for NLstepsize +% gamma; +% SS_dzmin; +% SS_dzmax; +% SS_dphimax; +% alpha_lin; +% +% %required for lin_step +% z_prop; +% lin_z_test; +% corr_length; +% n_plates_done; +% missing_dz; +% n_step = 0; +% brf; +% common_beta.X; +% common_beta.Y; +% alpha_lin.X; +% alpha_lin.X; +% +% %required fr nonlin step +% chi; +% manakov; +% beat_len ; +% alpha_lin.X; +% alpha_lin.Y; + + + % get nonlinear step size + [dz] = getNLstepsize(opt_x,opt_y,gamma,SS_dzmin,SS_dzmax,SS_dphimax,alpha_lin); + + n_step = 0; + z_prop = 0; + + while z_prop < L + + % reduce step length (dz) if we are to overshoot the fiber length + % (L) in the next step + if z_prop + dz > L + dz = L - z_prop; + end + + % update step number (n) + n_step=n_step+1; + + % half linear step + [opt_x,opt_y,z_prop,lin_z_test,... + corr_length,n_plates_done,missing_dz,n_step,... + brf,common_beta.X,... + common_beta.Y,alpha_lin.X,alpha_lin.X]... + = lin_step(... + opt_x,opt_y,z_prop,lin_z_test,... + dz/2,corr_length,n_plates_done,missing_dz,n_step,... + brf,common_beta.X,... + common_beta.Y,alpha_lin.X,alpha_lin.X); + + % complete nonlinear step + + [opt_x,opt_y] = nl_step(opt_x,opt_y, dz, gamma, chi, manakov, beat_len ,alpha_lin.X, alpha_lin.Y); + + % half linear step + [opt_x,opt_y,z_prop,lin_z_test,... + corr_length,n_plates_done,missing_dz,n_step,... + brf,common_beta.X,... + common_beta.Y,alpha_lin.X,alpha_lin.X]... + = lin_step... + (opt_x,opt_y,z_prop,lin_z_test,... + dz/2,corr_length,n_plates_done,missing_dz,n_step,... + brf,common_beta.X,... + common_beta.Y,alpha_lin.X,alpha_lin.X); + + % get nonlinear step size + [dz] = getNLstepsize(opt_x,opt_y,gamma,SS_dzmin,SS_dzmax,SS_dphimax,alpha_lin); + + end + + +end + diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index 14db6dd..37a39fa 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -21,8 +21,8 @@ laser_linewidth = 0e6; % EQ SETTINGS vnle_order1 = 50; -vnle_order2 = 0; -vnle_order3 = 0; +vnle_order2 = 3; +vnle_order3 = 3; vnle_order=[vnle_order1,vnle_order2,vnle_order3]; dfe_order = [0 0 0]; len_tr = 4096*2; @@ -34,15 +34,15 @@ mu_dc = 0.005; mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; mu_dfe = 0.0004; - -rcalpha = 0.05; -Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); - +%DB Stuff db_precode = 0; db_encode = 0; duob_mode = db_mode.no_db; 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); @@ -67,6 +67,7 @@ num_realiz = 1; gmi_vnle_bitwise = NaN(length(wavelengthplan),length(rop),num_realiz); snr_vnle= NaN(length(wavelengthplan),length(rop),num_realiz); ber_vnle= NaN(length(wavelengthplan),length(rop),num_realiz); +output = cell(length(wavelengthplan),length(rop),num_realiz); for realiz = 1:num_realiz @@ -166,30 +167,46 @@ for realiz = 1:num_realiz Rx_sig = Scpe_cell{1}; Rx_sig = Rx_sig.normalize("mode","rms"); - % 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_signal_sd, eq_noise] = eq_.process(Rx_sig, Symbols{l}); - showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ'); - [mi_gomez] = calc_air(eq_signal_sd, Symbols{l}, "skip_front", 100, "skip_end", 100); - [gmi_vnle_bitwise(l,ri,realiz)] = calc_ngmi(eq_signal_sd,Symbols{l}); - % [gmi_bitwise_2] = calc_gmi_bitwise(eq_signal_sd,Symbols{l}); - snr_vnle(l,ri,realiz) = calc_snr(Symbols{l}, eq_signal_sd-Symbols{l}); - - % eq_signal_sd.plot("displayname",'bla','fignum',199); - % eq_signal_sd.eye(fsym,M,"fignum",103837); - - eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); - rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); - [~,tot_err,ber_vnle(l,ri,realiz),a] = calc_ber(rx_bits.signal,Tx_bits{l}.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - burst_vnle = count_error_bursts(a, 10)./tot_err; - - % showLevelConfusionMatrix(eq_signal_hd,Symbols{l},"M",M,"fignum",200,"displayname",'bla'); - % showLevelScatter(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','f_sym',fsym,'fignum',201); - % show2Dconstellation(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','fignum',2241); - - fprintf('CH %d :BER VNLE: %.2e \n',l,ber_vnle(l,ri,realiz)); - fprintf('CH %d :NGMI VNLE: %.2f \n',l,gmi_vnle_bitwise(l,ri,realiz)./m); + + + + 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},... + "precode_mode",duob_mode,... + 'showAnalysis',0,... + "postFFE",[],... + "eth_style_symbol_mapping",0); + + output{l,ri,realiz} = ffe_results; + + + + % % 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_signal_sd, eq_noise] = eq_.process(Rx_sig, Symbols{l}); + % showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ'); + % [mi_gomez] = calc_air(eq_signal_sd, Symbols{l}, "skip_front", 100, "skip_end", 100); + % [gmi_vnle_bitwise(l,ri,realiz)] = calc_ngmi(eq_signal_sd,Symbols{l}); + % % [gmi_bitwise_2] = calc_gmi_bitwise(eq_signal_sd,Symbols{l}); + % snr_vnle(l,ri,realiz) = calc_snr(Symbols{l}, eq_signal_sd-Symbols{l}); + % + % % eq_signal_sd.plot("displayname",'bla','fignum',199); + % % eq_signal_sd.eye(fsym,M,"fignum",103837); + % + % eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); + % rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); + % [~,tot_err,ber_vnle(l,ri,realiz),a] = calc_ber(rx_bits.signal,Tx_bits{l}.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + % burst_vnle = count_error_bursts(a, 10)./tot_err; + % + % % showLevelConfusionMatrix(eq_signal_hd,Symbols{l},"M",M,"fignum",200,"displayname",'bla'); + % % showLevelScatter(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','f_sym',fsym,'fignum',201); + % % show2Dconstellation(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','fignum',2241); + % + % fprintf('CH %d :BER VNLE: %.2e \n',l,ber_vnle(l,ri,realiz)); + % fprintf('CH %d :NGMI VNLE: %.2f \n',l,gmi_vnle_bitwise(l,ri,realiz)./m); end diff --git a/projects/WDM/WDM_settings.m b/projects/WDM/WDM_settings.m index 49ddaf4..a0bfe85 100644 --- a/projects/WDM/WDM_settings.m +++ b/projects/WDM/WDM_settings.m @@ -1,3 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + % Add the imdd_simulation framework to the path if ispc addpath(genpath('C:\Users\Silas\Documents\MATLAB\imdd_simulation')); From cbc0ceff0a3f1d27ba7e5518788d39bff3acc68a Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 17 Sep 2025 16:19:53 +0200 Subject: [PATCH 14/30] changes in WDM pt5 --- projects/WDM/WDM_model.m | 151 ++++++++++++++++++------------------ projects/WDM/WDM_settings.m | 27 ++++++- 2 files changed, 103 insertions(+), 75 deletions(-) diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index 37a39fa..10e6c69 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -3,21 +3,26 @@ % --- FIRST LINE: evaluate settings located beside this script --- run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m')); +num_realiz = 1; +% wavelengthplan = calcWavelengthPlan(16,400e9,1310); +wavelengthplan = [1295,1305,1315,1325]; +link_length = 2; +pmd = 0.1; +gamma = 0.0023; + + M = 4; m = floor(log2(M)*10)/10; fsym = 224e9; fdac = 2*fsym; fadc = 2*fsym; -link_length = 0; -wavelengthplan = calcWavelengthPlan(16,400e9,1310); -wavelengthplan = [1295,1305,1315,1325]; random_key = 2; % Laser / Modulator vbias_rel = 0.5; u_pi = 3.2; vbias = -vbias_rel*u_pi; -laser_linewidth = 0e6; +laser_linewidth = 0e6; % EQ SETTINGS vnle_order1 = 50; @@ -43,7 +48,6 @@ 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); margin = 25e12; % some THz left and right @@ -63,14 +67,14 @@ Tx_bits = {}; rop = linspace(-11,0,8); -num_realiz = 1; -gmi_vnle_bitwise = NaN(length(wavelengthplan),length(rop),num_realiz); -snr_vnle= NaN(length(wavelengthplan),length(rop),num_realiz); -ber_vnle= NaN(length(wavelengthplan),length(rop),num_realiz); -output = cell(length(wavelengthplan),length(rop),num_realiz); +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); for realiz = 1:num_realiz + for l = 1:N [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(... @@ -118,9 +122,9 @@ for realiz = 1:num_realiz Dvec = getDispersionVector(nSegments, D_local, zdw, randomize_D, random_key+realiz); for s = 1:nSegments - Opt_sig_wdm_fib = DP_Fiber("L",link_length/nSegments,"D",Dvec(s),"Dpmd",0.1,"Ds",0.06,... + Opt_sig_wdm_fib = DP_Fiber("L",link_length/nSegments,"D",Dvec(s),"Dpmd",pmd,"Ds",0.07,... "beat_len",10,"corr_len",100,"dz",1,"manakov",0,... - "gamma",0.0023,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01,... + "gamma",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 @@ -169,56 +173,85 @@ 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},... "precode_mode",duob_mode,... 'showAnalysis',0,... "postFFE",[],... "eth_style_symbol_mapping",0); - output{l,ri,realiz} = ffe_results; + output_ffe{l,ri,realiz} = ffe_results; - % % 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_signal_sd, eq_noise] = eq_.process(Rx_sig, Symbols{l}); - % showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ'); - % [mi_gomez] = calc_air(eq_signal_sd, Symbols{l}, "skip_front", 100, "skip_end", 100); - % [gmi_vnle_bitwise(l,ri,realiz)] = calc_ngmi(eq_signal_sd,Symbols{l}); - % % [gmi_bitwise_2] = calc_gmi_bitwise(eq_signal_sd,Symbols{l}); - % snr_vnle(l,ri,realiz) = calc_snr(Symbols{l}, eq_signal_sd-Symbols{l}); - % - % % eq_signal_sd.plot("displayname",'bla','fignum',199); - % % eq_signal_sd.eye(fsym,M,"fignum",103837); - % - % eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); - % rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); - % [~,tot_err,ber_vnle(l,ri,realiz),a] = calc_ber(rx_bits.signal,Tx_bits{l}.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % burst_vnle = count_error_bursts(a, 10)./tot_err; - % - % % showLevelConfusionMatrix(eq_signal_hd,Symbols{l},"M",M,"fignum",200,"displayname",'bla'); - % % showLevelScatter(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','f_sym',fsym,'fignum',201); - % % show2Dconstellation(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','fignum',2241); - % - % fprintf('CH %d :BER VNLE: %.2e \n',l,ber_vnle(l,ri,realiz)); - % fprintf('CH %d :NGMI VNLE: %.2f \n',l,gmi_vnle_bitwise(l,ri,realiz)./m); - + %VNLE + 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); + 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); + end + + [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig, Symbols{l},Tx_bits{l}, ... + "precode_mode", duob_mode,... + 'showAnalysis', 0, ... + "postFFE", [],... + "eth_style_symbol_mapping", 0); + + output_vnle{l,ri,realiz} = vnle_results; + 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); + else + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(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}, ... + "precode_mode", duob_mode, ... + 'showAnalysis', 0,... + "postFFE", []); + + output_dbt{l,ri,realiz} = dbt_results; + end end + res = struct(); + res.ffe = output_ffe; + res.vnle = output_vnle; + res.mlse = output_mlse; + res.dbt = output_dbt; + + % Save results + save(fullfile(output_root, fname), 'res', '-v7.3'); + fprintf('Saved results to: %s\n', fullfile(output_root, fname)); + 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,squeeze(ber_vnle(l,:,:)),'Marker','*','DisplayName',sprintf('Ch: %d',wavelengthplan(l)),'Color',cols(l,:),'HandleVisibility','on') + % 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, output_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, output_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'); @@ -228,38 +261,8 @@ set(gca, 'XScale', 'linear', ... 'YScale', 'log', ... 'TickLabelInterpreter', 'latex', ... 'FontSize', 11); - - -res = struct(); % placeholder -res.gmi_vnle_bitwise = gmi_vnle_bitwise; % placeholder -res.snr_vnle = snr_vnle; -res.ber_vnle = ber_vnle; - -% --------------------------------------------- - -% result filename (timestamp + optional job id) -t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); -jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end -host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end - -% Output directory depends on platform -if ispc - output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\'); -else - output_root = '/work_beegfs/sutef391/results_WDM'; -end -if ~exist(output_root,'dir'), mkdir(output_root); end - -% Build filename -t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); -jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end -host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end - -fname = sprintf('WDM_%s_%s_%s.mat', char(t), host, jobid); - -% Save results -save(fullfile(output_root, fname), 'res', '-v7.3'); -fprintf('Saved results to: %s\n', fullfile(output_root, fname)); +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 @@ -279,7 +282,7 @@ function dispersion_vector = getDispersionVector(N, D, ref_zdw, randomize_ZDW, r % constants (matching the Python code) meanLambda_nm = 1310; % center wavelength sigma_nm = 2; % ZDW sigma - Dslope = 0.09; % ps/(nm·km) per nm detuning + Dslope = 0.07; % ps/(nm·km) per nm detuning % random ZDW-induced dispersion offset if randomize_ZDW diff --git a/projects/WDM/WDM_settings.m b/projects/WDM/WDM_settings.m index a0bfe85..68a04a3 100644 --- a/projects/WDM/WDM_settings.m +++ b/projects/WDM/WDM_settings.m @@ -52,4 +52,29 @@ if isempty(p) || p.NumWorkers ~= cpus if ~isempty(p), delete(p); end p = parpool(c, cpus); % avoids the “queued” state end -fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation); \ No newline at end of file +fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation); + + + + + + +% result filename (timestamp + optional job id) +t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); +jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end +host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end + +% Output directory depends on platform +if ispc + output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\'); +else + output_root = '/work_beegfs/sutef391/results_WDM'; +end +if ~exist(output_root,'dir'), mkdir(output_root); end + +% Build filename +t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); +jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end +host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end + +fname = sprintf('WDM_%s_%s_%s.mat', char(t), host, jobid); \ No newline at end of file From 2c0e7a81aa527bcfa547b688d1003ced3b659a5e Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 17 Sep 2025 17:17:03 +0200 Subject: [PATCH 15/30] WDM update : demux changes for memory optimizaion --- Classes/02_optical/Optical_Demultiplex.m | 31 ++++++++++++++++++++++-- projects/WDM/WDM_model.m | 12 ++++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Classes/02_optical/Optical_Demultiplex.m b/Classes/02_optical/Optical_Demultiplex.m index fc2bc36..7d7c39e 100644 --- a/Classes/02_optical/Optical_Demultiplex.m +++ b/Classes/02_optical/Optical_Demultiplex.m @@ -113,8 +113,35 @@ classdef Optical_Demultiplex < handle pha = mod(-2*pi*(0:blocklen_in-1).'.*df_T/obj.fs_in, 2*pi); lo = cos(pha)+1i*sin(pha); - x_envelopes = ifft(fft(att.*signal_in(:,1).*lo).*H); - y_envelopes = ifft(fft(att.*signal_in(:,2).*lo).*H); + % x_envelopes = ifft(fft(att.*signal_in(:,1).*lo).*H); + % y_envelopes = ifft(fft(att.*signal_in(:,2).*lo).*H); + + N = size(lo,1); + C = size(lo,2); + + x_envelopes = zeros(N, C, 'like', signal_in); + y_envelopes = zeros(N, C, 'like', signal_in); + + s1 = signal_in(:,1); + s2 = signal_in(:,2); + + % Reusable work buffers (avoid reallocations) + wrk_time = zeros(N,1, 'like', signal_in); + wrk_freq = zeros(N,1, 'like', signal_in); + + for c = 1:C + % ---- X branch ---- + wrk_time(:) = att .* s1 .* lo(:,c); % N×1 + wrk_freq(:) = fft(wrk_time); % N×1 + wrk_freq(:) = wrk_freq .* H; % N×1 + x_envelopes(:,c) = ifft(wrk_freq); % N×1 + + % ---- Y branch ---- + wrk_time(:) = att .* s2 .* lo(:,c); + wrk_freq(:) = fft(wrk_time); + wrk_freq(:) = wrk_freq .* H; + y_envelopes(:,c) = ifft(wrk_freq); + end end end diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index 10e6c69..9563ef6 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -3,7 +3,7 @@ % --- FIRST LINE: evaluate settings located beside this script --- run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m')); -num_realiz = 1; +num_realiz = 50; % wavelengthplan = calcWavelengthPlan(16,400e9,1310); wavelengthplan = [1295,1305,1315,1325]; link_length = 2; @@ -65,7 +65,7 @@ signal_cell = {}; Symbols = {}; Tx_bits = {}; -rop = linspace(-11,0,8); +rop = -8.25:0.75:0; output_ffe = cell(length(wavelengthplan),length(rop),num_realiz); output_vnle = cell(length(wavelengthplan),length(rop),num_realiz); @@ -75,7 +75,7 @@ output_dbt = cell(length(wavelengthplan),length(rop),num_realiz); for realiz = 1:num_realiz - for l = 1:N + parfor l = 1:N [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(... "fsym",fsym,"M",M,"order",18,"useprbs",0,... @@ -103,7 +103,7 @@ for realiz = 1:num_realiz signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",30).process(Eml_out); end - Opt_sig_wdm = Optical_Multiplex("fs_in",signal_cell{l}.fs,"fs_out",upsample_pow*Eml_out.fs,... + Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover,... "lambda_center",1310,"random_key",0,"filtype",1,"B",200e9).process(signal_cell); Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",3+10*log10(N)).process(Opt_sig_wdm); @@ -250,8 +250,8 @@ 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, output_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, output_mlse(l,:), 'UniformOutput', true),'Marker','o','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'); From 9d63457030eae95631f5168842334019e66dfc95 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Fri, 26 Sep 2025 14:25:52 +0200 Subject: [PATCH 16/30] ECOC Theroy stuff --- Classes/04_DSP/Sequence Detection/MLSE.m | 6 +- projects/ECOC_2025/load_signal_standalone.m | 42 +-- .../plots_from_database/plot_mpi_trial.m | 86 +++--- .../theory/analytic_mpi_evaluation.m | 19 +- .../ECOC_2025/theory/coherence_length_plot.m | 38 +-- .../Auswertung_JLT/ber_vs_rate.m | 4 +- projects/IMDD_base_system/simulation_bwl.m | 4 +- projects/WDM/WDM_auswertung.m | 251 ++++++++++++++++++ projects/WDM/WDM_model.m | 122 ++++----- 9 files changed, 397 insertions(+), 175 deletions(-) create mode 100644 projects/WDM/WDM_auswertung.m diff --git a/Classes/04_DSP/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index e51d61b..c25654c 100644 --- a/Classes/04_DSP/Sequence Detection/MLSE.m +++ b/Classes/04_DSP/Sequence Detection/MLSE.m @@ -54,9 +54,9 @@ classdef MLSE < handle function [VITERBI_ESTIMATION_SYMBOLS,LLR_maxlogmap,GMI] = process_(obj,data_in,data_ref) - debug = 0; + debug = 1; - 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) + trellis_state_mode = 0; % 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; @@ -65,7 +65,7 @@ classdef MLSE < handle trellis_exclusion = 0; % PAM-6 only (only if data is NOT precoded!) - scale_mode = 2; % scale_mode: + scale_mode = 0; % scale_mode: % 0 = no scaling, % 1 = RMS→scale MODEL, % 2 = MMSE/time-corr→scale MODEL, 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/plots_from_database/plot_mpi_trial.m b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m index 297f17b..68ac774 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,57 @@ -% 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); - -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); +fp = QueryFilter(); +% fp.where('Runs', 'loop_id','EQUALS', 209); +% fp.where('Runs', 'sir','EQUALS', 21); +fp.where('Runs', 'pam_level','EQUALS', 4); +fn = [db.getTableFieldNames('Runs');db.getTableFieldNames('Results')]; +[dataTable,sql_query] = db.queryDB(fp,fn); %% -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); 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_rate.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m index d39a24c..1c39b25 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 = 4; fp.where('Runs', 'pam_level','EQUALS', M); % fp.where('Runs', 'bitrate','LESS_THAN', 310e9); fp.where('Runs', 'fiber_length','EQUALS', 2); @@ -13,7 +13,7 @@ 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); diff --git a/projects/IMDD_base_system/simulation_bwl.m b/projects/IMDD_base_system/simulation_bwl.m index 40ccbb2..79fc4c7 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; @@ -48,7 +48,7 @@ cols = linspecer(6); rop = [-6]; bwl = [0.5:0.1:1.5]; fsym = [120:8:256].*1e9; -fsym =150e9; +fsym =210e9; ber_vnle = []; ber_mlse = []; diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m new file mode 100644 index 0000000..32c6cee --- /dev/null +++ b/projects/WDM/WDM_auswertung.m @@ -0,0 +1,251 @@ +figure(); hold on; +cols = cbrewer2('set2',N); % one color per wavelength (Ch) + +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); + +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 9563ef6..faa74e6 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -2,23 +2,22 @@ % TX % --- FIRST LINE: evaluate settings located beside this script --- run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m')); +s = struct; +s.num_realiz = 1; +% s.wavelengthplan = calcWavelengthPlan(16,400e9,1310); +s.wavelengthplan = [1295,1305,1315,1325]; +s.link_length = 2; +s.pmd = 0.0; +s.gamma = 0.00; -num_realiz = 50; -% wavelengthplan = calcWavelengthPlan(16,400e9,1310); -wavelengthplan = [1295,1305,1315,1325]; -link_length = 2; -pmd = 0.1; -gamma = 0.0023; - - -M = 4; -m = floor(log2(M)*10)/10; +s.M = 4; +m = floor(log2(s.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; @@ -48,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; @@ -58,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 %%%%%% @@ -97,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 +118,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",link_length/nSegments,"D",Dvec(s),"Dpmd",pmd,"Ds",0.07,... + Opt_sig_wdm_fib = DP_Fiber("L",s.link_length/nSegments,"D",Dvec(seg),"Dpmd",s.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,23 +132,23 @@ 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); PD_cell = {}; - for l = 1:N + parfor l = 1:N %%%%%% 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 +175,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 +193,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 +208,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 +230,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 +239,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 From a0ae47a2a0ece6c73280b53de15bfaf6471a7f17 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 8 Oct 2025 09:50:14 +0200 Subject: [PATCH 17/30] WDM stuff --- projects/WDM/WDM_auswertung.m | 7 ++++--- projects/WDM/WDM_model.m | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m index 32c6cee..d13c97f 100644 --- a/projects/WDM/WDM_auswertung.m +++ b/projects/WDM/WDM_auswertung.m @@ -1,16 +1,17 @@ -figure(); hold on; -cols = cbrewer2('set2',N); % one color per wavelength (Ch) + try rop = res.settings.rop; % 12 points wavelengthplan = res.settings.wavelengthplan; catch wavelengthplan = [1295,1305,1315,1325]; - % wavelengthplan = calcWavelengthPlan(16,400e9,1310); + 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; diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index faa74e6..c72a985 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -4,7 +4,7 @@ run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m')); s = struct; s.num_realiz = 1; -% s.wavelengthplan = calcWavelengthPlan(16,400e9,1310); +s.wavelengthplan = calcWavelengthPlan(16,400e9,1310); s.wavelengthplan = [1295,1305,1315,1325]; s.link_length = 2; s.pmd = 0.0; From 99898da51954a6f94a1a1d4b1c0acecba7a8cefe Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 8 Oct 2025 09:51:47 +0200 Subject: [PATCH 18/30] MLSE has some more class settings DSP auswertung weiter geschrieben --- Classes/04_DSP/Sequence Detection/MLSE.m | 30 +- Functions/EQ_structures/dsp_runid.m | 20 +- .../Auswertung_JLT/gmi_vs_rate.m | 9 +- .../Auswertung_JLT/plot_measurements_gpt.m | 340 ++++++++++++++++++ .../Auswertung_JLT/run_dsp_from_db.m | 2 +- .../Auswertung_JLT/run_plot_measurements.m | 48 +++ 6 files changed, 438 insertions(+), 11 deletions(-) create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m diff --git a/Classes/04_DSP/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index c25654c..3a8e288 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 = 1; + + arguments + obj + data_in + data_ref + end - trellis_state_mode = 0; % 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 = 0; % scale_mode: + scale_mode = obj.scale_mode; % scale_mode: % 0 = no scaling, % 1 = RMS→scale MODEL, % 2 = MMSE/time-corr→scale MODEL, diff --git a/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m index 09e5e27..132cc75 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -201,7 +201,18 @@ try 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, ... @@ -253,7 +264,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); 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/plot_measurements_gpt.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m new file mode 100644 index 0000000..0b5cefd --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m @@ -0,0 +1,340 @@ +function h = plot_measurements_gpt(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' , 3, ... + 'pct_limits' , [2.5 97.5], ... + 'min_pts_x' , 3, ... + 'show_raw' , true, ... + 'show_precoded', [], ... + 'show_spread' , 'none', ... + 'fec_lines' , [2.2e-4 4.85e-3 2e-2], ... + '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 ... +); +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 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); + +%% ---- Plotting +figure; hold on; grid 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); 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); + end + end + + % sort + [xu, ord] = sort(xu); + yu = yu(ord); ylo = ylo(ord); yhi = yhi(ord); + + % Styles + pre = logical(grpTbl.pre_emph(gi)); + ls = tern(pre, cfg.plot.lineStyle_pre_emph_on, cfg.plot.lineStyle_pre_emph_off); + 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', 5, ... + 'Color', colL, 'LineStyle', ls, ... + 'DisplayName', char(lbl)); + + % Spread (IQR) in line color + if strcmpi(cfg.show_spread,'iqr') && 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); end + end + h.lines_p(gi) = plot(xu, ypu, ... + 'LineWidth', max(1.2, cfg.plot.lineWidth-0.2), ... + 'Marker', cfg.plot.marker_precoded, 'MarkerSize', 5, ... + '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; + +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-4, 0.3]); +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 = log2(double(T.pam_level)); + x = (T.symbolrate .* bits) * 1e-9; + label = 'Bitrate [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,'pre-emph on','pre-emph off'); end + parts(i) = sprintf('%s=%s', key, string(val)); +end +s = strjoin(parts, ', '); +end + +function [cols_line, cols_scatter] = buildGroupColors(nG, plotcfg) +% Build paired colors (dark for lines, light for scatter) using cbrewer2('Paired') +useBrewer = plotcfg.use_cbrewer2 && exist('cbrewer2','file')==2; +if useBrewer + N = max(2*nG, 12); % ensure pairs available + 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 + % Fallback: lines() + lightened scatter + 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.5); % 50% toward white + 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/run_dsp_from_db.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m index 2a82dc6..8da64d8 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m @@ -44,7 +44,7 @@ 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', 'fiber_length','EQUALS', 1); fp.where('Runs', 'is_mpi','EQUALS', 0); % fp.where('Runs', 'interference_path_length','EQUALS', 1000); % fp.where('Runs', 'loop_id','GREATER_THAN', 11); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m new file mode 100644 index 0000000..e7dd62f --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m @@ -0,0 +1,48 @@ +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 = 4; +fp = QueryFilter(); +% fp.where('Runs', 'run_id','EQUALS', 987); +fp.where('Runs', 'pam_level','EQUALS', M); +fp.where('Runs', 'symbolrate','EQUALS', 150e9); +% 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','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_new')]; +[dataTable,~] = db.queryDB(fp, fields); + +cfg = struct; +cfg.x_axis = 'accumulated_dispersion'; % 'symbolrate' | 'baudrate' | 'bitrate' | 'wavelength' +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.vnle,equalizer_structure.ffe,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle_db_mlse,equalizer_structure.dfe]); + +cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise +cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available +cfg.show_raw = true; +cfg.show_spread = 'none'; % 'none' or 'iqr' +cfg.agg = 'mean'; % or 'median' +cfg.show_precoded = 0; +cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional + +% 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); \ No newline at end of file From b5387f78c686571ad19f2587a660eee18acaacc3 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Fri, 17 Oct 2025 08:02:30 +0200 Subject: [PATCH 19/30] Dispersion theory scripts --- Functions/Theory/dispersion_10km.m | 90 ++++++++++++++ .../Theory/dispersion_wavelength_notch.m | 32 +++++ Functions/Theory/dispersion_wdm.m | 111 ++++++++++++++++++ Functions/Theory/power_fading.m | 40 +++++++ Functions/convert_freq_lambda.m | 62 ++++++++++ 5 files changed, 335 insertions(+) create mode 100644 Functions/Theory/dispersion_10km.m create mode 100644 Functions/Theory/dispersion_wavelength_notch.m create mode 100644 Functions/Theory/dispersion_wdm.m create mode 100644 Functions/Theory/power_fading.m create mode 100644 Functions/convert_freq_lambda.m diff --git a/Functions/Theory/dispersion_10km.m b/Functions/Theory/dispersion_10km.m new file mode 100644 index 0000000..87a17a8 --- /dev/null +++ b/Functions/Theory/dispersion_10km.m @@ -0,0 +1,90 @@ +%% ============================================================ +% IM/DD Fading Notch – λ_null vs. Bandwidth (Fixed 10 km) +% ============================================================ + + + +%% Fiber and dispersion parameters +lambda0 = 1315e-9; % Zero-dispersion wavelength [m] +S0 = 0.08; % 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 + +%% ------------------------------------------------------------ +% Plot λ_null vs. f_null for 10 km fiber +% ------------------------------------------------------------ +% figure('Color','w'); +% plot(lambda_nm,f_GHz, 'LineWidth', 2); +% grid on; box on; + +cols = cbrewer2('Paired',10); +figure('Color','w');hold on + +plot(lambda_nm, f_GHz, 'LineWidth',2,'DisplayName',sprintf('%d km',L),'Color',cols(2,:)); + +yticks([56,75,90,112]) + +f_GHz = [56,75,90,112] * 1e9; +[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_GHz, L, lambda0, S0); +lambda_nm = lambda_vec * 1e9; % Convert to nm + +xticks(round(lambda_nm)) + +xlabel('$\Delta \lambda$ from ZDW [nm]'); +ylabel('$F_{null}$ [GHz]'); +grid on; box on; +lim=(lambda0.*1e9)-[8,40]; +xlim([lim(2) lim(1)]); +% ylim([40,130]) + +%% ------------------------------------------------------------ +% 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 [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); + + % Normal-dispersion branch (λ < λ0) + fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS; + + % Solve within normal-dispersion range + try + lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]); + catch + lambda_sol = lambda_min; + end + + % Clamp to O-band + 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 + Dacc_val = min(max(Dacc_val, -100), 100); + + Dacc_vec(k) = Dacc_val; +end +end 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/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/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 From 3ea64399478515347548640add7d75993f84e10c Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Fri, 17 Oct 2025 11:50:27 +0200 Subject: [PATCH 20/30] - bring FWM plots back to life! - some dispersion plots with the great help of chatGPT :-D --- Classes/DataBaseHandler/DBHandler.m | 5 +- Classes/Warehouse_class/classes/DataStorage.m | 2 +- Classes/Warehouse_class/classes/Parameter.m | 7 + .../functions/fwm_plots/automate_JLT_plots.m | 69 +++++--- .../fwm_plots/automate_PTL_plot_new.m | 137 +++++++++++++++ .../functions/fwm_plots/plotCurve.m | 62 ++++--- .../functions/fwm_plots/plotViolin.m | 2 +- Functions/Theory/dispersion_contour.m | 5 +- .../dispersion_contour_bandwidth_lambda.m | 146 ++++++++++++++++ .../Theory/dispersion_first_notch_10km.m | 38 ++++ Functions/Theory/dispersion_power_fading.m | 165 ++++++++++++++++++ Functions/Theory/matched_filter_rrc.m | 120 +++++++++++++ .../plots_from_database/plot_mpi_trial.m | 24 ++- 13 files changed, 720 insertions(+), 62 deletions(-) create mode 100644 Classes/Warehouse_class/classes/Parameter.m create mode 100644 Classes/Warehouse_class/functions/fwm_plots/automate_PTL_plot_new.m create mode 100644 Functions/Theory/dispersion_contour_bandwidth_lambda.m create mode 100644 Functions/Theory/dispersion_first_notch_10km.m create mode 100644 Functions/Theory/dispersion_power_fading.m create mode 100644 Functions/Theory/matched_filter_rrc.m diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index f1ca74e..5569746 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -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/Warehouse_class/classes/DataStorage.m b/Classes/Warehouse_class/classes/DataStorage.m index 5fd288d..93a7cf4 100644 --- a/Classes/Warehouse_class/classes/DataStorage.m +++ b/Classes/Warehouse_class/classes/DataStorage.m @@ -137,7 +137,7 @@ classdef DataStorage < handle if ~isempty(tmp) if isa(tmp,'double') - value(i) = tmp ; + value(i,:) = tmp ; 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/automate_JLT_plots.m b/Classes/Warehouse_class/functions/fwm_plots/automate_JLT_plots.m index 68b77b6..cc2c8d6 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/automate_JLT_plots.m +++ b/Classes/Warehouse_class/functions/fwm_plots/automate_JLT_plots.m @@ -5,6 +5,16 @@ 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; @@ -17,15 +27,15 @@ plotJob.d = 0; plotJob.sgm = 0; plotJob.pol = "copolarized"; plotJob.p_in = 3; -plotJob.gamma = 0; -plotJob.pmd = 0; +plotJob.gamma = 0.0023; +plotJob.pmd = 0.1; plotJob.channelspacing = 400e9; plotJob.randzdw = 0; -plotJob.plot_ber_curve = 1; +plotJob.plot_ber_curve = 0; plotJob.plot_3dber_curve = 0; -plotJob.plot_violin = 0; +plotJob.plot_violin = 1; plotJob.plot_wavelength_sweep = 0; plotJob.plot_wavelength_sweep_failure_rate = 0; @@ -34,13 +44,20 @@ plotJob.dataStatArg = 'Lineplot with quartiles'; plotJob.plotTypeArg = 'Lines'; plotJob.displayname = 'a'; plotJob.title = 'title'; -plotJob.figName = '16 Chann__'; +plotJob.figName = '16 Chann'; plotJob.xAxisLabel = 'ROP per Channel in dBm'; plotJob.yAxisLabel = 'BER'; -% createbercurves(wh,plotJob) -createviolinplots(wh,plotJob); -% createsweepplots(wh,plotJob); +%% + + % createbercurves(wh,plotJob) + + P = [3,6]; + for i = 1:2 + plotJob.p_in = P(i); + createviolinplots(wh,plotJob); + end + % createsweepplots(wh,plotJob); %% 1 @@ -64,7 +81,7 @@ 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]; +P_launch = [3,6]; fig = figure('Name',plotJob.figName); fig.Position = plotJob.Position; @@ -81,7 +98,7 @@ for idx = 1:(numRows * numCols) plotJob.sgm = Sgm(idx); plotJob.randzdw = 1; - for i = 1:3 + for i = 1:length(P_launch) plotJob.p_in = P_launch(i); plotJob.color = cols(colidx(i),:); @@ -199,6 +216,7 @@ end %% 2 function createviolinplots(wh,plotJob) + width = 350; height = 200; s = 100; @@ -208,30 +226,34 @@ cols = cbrewer2("paired",12); numRows = 1; numCols = 4; - plotJob.ch = 16; -plotJob.p_in = 3; -plotJob.randzdw = 0; +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 = [2]; -Len = [2]; +colidx = [3]; +Len = [10]; -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]; +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 -t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact'); for idx = 1:(numRows * numCols) % Create subplot - %subplot(numRows, numCols, idx); - nexttile; + subplot(numRows, numCols, idx); plotJob.pol = Pol(idx); plotJob.d = D(idx); @@ -295,7 +317,6 @@ annotation(fig,'textbox',... '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'; diff --git a/Classes/Warehouse_class/functions/fwm_plots/automate_PTL_plot_new.m b/Classes/Warehouse_class/functions/fwm_plots/automate_PTL_plot_new.m new file mode 100644 index 0000000..f92f86f --- /dev/null +++ b/Classes/Warehouse_class/functions/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/plotCurve.m b/Classes/Warehouse_class/functions/fwm_plots/plotCurve.m index a7134c9..e06e7e5 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotCurve.m +++ b/Classes/Warehouse_class/functions/fwm_plots/plotCurve.m @@ -6,7 +6,7 @@ fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); if isvalid(fig) figure(fig) - fig = get(fig); + % fig = get(fig); AxesMain = fig.CurrentAxes; hold on else @@ -28,32 +28,39 @@ 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) = max(temp,[],'all'); + ber(xl) = quantile(temp,0.9,"all"); + % ber(xl) = max(temp,[],'all'); linew = 1.0; - markersz = 3; - linestyle = '-'; + 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 = 3; - linestyle = '--'; + 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 = 3; - linestyle = '-'; + 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); @@ -63,7 +70,8 @@ for xl = 1:numel(xAxis) linew = 1; markersz = 1; - linestyle = '-'; + markerstyle = plotJob.markerstyle; + linestyle = plotJob.linestyle; elseif string(plotJob.dataStatArg) == "Lineplot with quartiles" @@ -75,14 +83,16 @@ for xl = 1:numel(xAxis) 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) = 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))); @@ -149,6 +159,7 @@ elseif string(plotJob.plotTypeArg) == "Lines" % [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)") @@ -160,6 +171,7 @@ elseif string(plotJob.plotTypeArg) == "Lines" 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'); @@ -169,22 +181,25 @@ elseif string(plotJob.plotTypeArg) == "Lines" 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,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 = 3; + hl.MarkerSize = 2; set(hp,'HandleVisibility','off'); %hp.LineWidth = 1.2; ho = outlinebounds(hl,hp); - set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.5); + 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',"o",'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]); + 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: "; @@ -245,9 +260,10 @@ grid minor fontsize(AxesMain,8,"points") % fontname(AxesMain,"Arial") -fig.Position = plotJob.Position; -fig.Units = "centimeters"; -fig.Position = [2 2 8.5 7]; + +% fig.Position = plotJob.Position; +% fig.Units = "centimeters"; +% fig.Position = [2 2 8.5 7]; set(AxesMain,'TickLabelInterpreter','latex') @@ -257,7 +273,7 @@ set(AxesMain.Legend,'Interpreter','latex') ylim([1e-5,0.3]); -xlim([min(xAxis),-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") diff --git a/Classes/Warehouse_class/functions/fwm_plots/plotViolin.m b/Classes/Warehouse_class/functions/fwm_plots/plotViolin.m index 2453744..93cfc10 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotViolin.m +++ b/Classes/Warehouse_class/functions/fwm_plots/plotViolin.m @@ -155,7 +155,7 @@ ylabel('Penalty in dB'); xlabel('Channel Number'); ylim([-9.3,-3]); xlim([0,plotJob.ch+1]); -grid minor; +% grid minor; set(gca, 'color', 'none'); legend = []; 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/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/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m index 68ac774..276c7d2 100644 --- a/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m +++ b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m @@ -43,12 +43,13 @@ db = DBHandler("type","mysql","dataBase",'labor'); fp = QueryFilter(); -% fp.where('Runs', 'loop_id','EQUALS', 209); -% fp.where('Runs', 'sir','EQUALS', 21); -fp.where('Runs', 'pam_level','EQUALS', 4); -fn = [db.getTableFieldNames('Runs');db.getTableFieldNames('Results')]; +% 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); + %% dataTable_clean = dataTable; @@ -74,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; @@ -132,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 @@ -190,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 @@ -244,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 @@ -294,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 From 7085ba093133c39566c335f7c78f773a1b1efd5c Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Fri, 24 Oct 2025 16:58:06 +0200 Subject: [PATCH 21/30] Gif stuff nonlinear MLSE investigation trying hard to implment ML-based pre Equalization to find the branch metrics --- Classes/00_signals/Signal.m | 52 ++-- Classes/04_DSP/Equalizer/FFE.m | 2 +- Classes/04_DSP/Equalizer/FFE_MLSE.m | 233 ++++++++++++++ Classes/04_DSP/Equalizer/ML_MLSE.m | 246 +++++++++++++++ Classes/04_DSP/Sequence Detection/MLSE.m | 114 ++++++- Classes/GifWriter.m | 146 +++++++++ Functions/EQ_structures/dsp_runid.m | 6 +- Functions/EQ_visuals/showEQNoisePSD.m | 2 +- Functions/Theory/CCDM/ccdm_gpt_example.m | 27 ++ Functions/beautifyBERplot.m | 13 +- .../Auswertung_JLT/run_dsp_from_db.m | 16 +- .../Auswertung_JLT/run_plot_measurements.m | 2 +- projects/IMDD_base_system/simulation_bwl.m | 286 ++++++++++++------ projects/IMDD_base_system/simulation_bwl_2.m | 8 +- projects/ML_based_MLSE/model.m | 194 ++++++++++++ .../Nonlinear_MLSE/simulation_nonlin_dsp.m | 229 ++++++++++++++ 16 files changed, 1414 insertions(+), 162 deletions(-) create mode 100644 Classes/04_DSP/Equalizer/FFE_MLSE.m create mode 100644 Classes/04_DSP/Equalizer/ML_MLSE.m create mode 100644 Classes/GifWriter.m create mode 100644 Functions/Theory/CCDM/ccdm_gpt_example.m create mode 100644 projects/ML_based_MLSE/model.m create mode 100644 projects/Nonlinear_MLSE/simulation_nonlin_dsp.m diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index c84ebf6..159b4e3 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -344,7 +344,7 @@ classdef Signal arguments obj - options.fignum + options.fignum = 2025 options.displayname = ""; options.color = []; options.normalizeToNyquist = 0; @@ -449,21 +449,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 +965,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.0025; + minA = 0; difference= maxA-minA; @@ -1012,7 +1016,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 +1125,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/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..e7a4bbd --- /dev/null +++ b/Classes/04_DSP/Equalizer/FFE_MLSE.m @@ -0,0 +1,233 @@ +classdef ML_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 = ML_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..db6c686 --- /dev/null +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -0,0 +1,246 @@ +classdef ML_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 = ML_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 + ML-Based Branch Metric Estimation + Viterbi + % ============================================================== + + % --- 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)); % alphabet size + L = obj.L; % MLSE memory + Nf = L; % filter length + Delta = ceil(L/2); % delay parameter + nStates = S^L; + nFeasible = S^(L-1)*S; + + % --- Trellis mapping + 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); + + % --- Valid transitions + valid = false(nStates); + for from = 1:nStates + for to = 1:nStates + if all(combs(to,2:end) == combs(from,1:end-1)) + valid(to,from) = true; + end + end + end + [valid_to, valid_from] = find(valid); + + % --- Noise estimation + y_ideal = conv(d(:), obj.DIR(:), "same"); + sigma2 = mean(abs(y - y_ideal).^2); + inv2s2 = 1/(2*sigma2); + + % --- Allocate vectors and weights + pm = zeros(nStates,1); + w = zeros(Nf,nFeasible); % filter weights per transition + b = zeros(1,nFeasible); % bias terms + v_hat = zeros(1,nFeasible); + v_tilde = zeros(1,nFeasible); + bm_vec = zeros(1,nFeasible); + zi = zeros(max(numel(obj.DIR)-1,0),1); + end + + % ============================================================== + % RUNTIME LOOP + % ============================================================== + 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 / FFE adaptation + if training + d_hat = d(symbol); + else + [~,idx] = min(abs(y(symbol) - obj.constellation)); + d_hat = obj.constellation(idx); + end + err = d_hat - y(symbol); + obj.e = obj.e + mu * (err * U); + + % --- Whitening + MLSE in last epoch + if epoch == epochs && ~training + + [y_white(symbol), zi] = filter(obj.DIR,1,y(symbol), zi); + k = symbol; + + % --- Build Δ-delayed observation window y_k + i1 = k - Nf + 1 + Delta; + i2 = k + Delta; + buf = y_white(max(1,i1):min(length(y_white),i2)); + padL = max(0,1 - i1); + padR = max(0,i2 - length(y_white)); + yk = [zeros(padL,1); buf(:); zeros(padR,1)]; % Nf×1 + + % --- Predict branch metrics for all feasible transitions + v_hat = (yk.' * w) + b; % [1×nFeasible] + v_hat = v_hat.'; % [nFeasible×1] + + % --- Extended path metrics + v_tilde = pm(valid_from) + v_hat; % [nFeasible×1] + + % --- Compute branch metrics (distance) + bm_vec = -(y_white(k) - v_hat).^2 * inv2s2; % 1×nFeasible + + % --- Survivor selection (vector aggregation) + pm_new_vec = pm(valid_from) + bm_vec.'; % nFeasible×1 + pm_next = -inf(nStates,1); + surv_idx = zeros(nStates,1); + for t = 1:nStates + mask = (valid_to==t); + [pm_next(t), arg] = max(pm_new_vec(mask)); + surv_idx(t) = valid_from(find(mask,1,'first')-1+arg); + end + pm = pm_next; + + % --- Traceback + if mod(symbol,obj.traceback_depth) == 0 + [~,viterbi_path(symbol)] = max(pm); + for n = symbol:-1:symbol-obj.traceback_depth+2 + viterbi_path(n-1) = surv_idx(viterbi_path(n)); + end + end + end + end + + % --- Output reconstructed path + 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/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index 3a8e288..fabee6f 100644 --- a/Classes/04_DSP/Sequence Detection/MLSE.m +++ b/Classes/04_DSP/Sequence Detection/MLSE.m @@ -277,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); @@ -564,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/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/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m index 132cc75..1a20686 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -96,10 +96,10 @@ try adaption= 1; use_dd_mode = 1; - use_ffe = 1; - use_dfe = 1; + use_ffe = 0; + use_dfe = 0; use_vnle_mlse = 1; - use_dbtgt = 1; + use_dbtgt = 0; use_dbenc = 0; addProcessingResultToDatabase = 0; diff --git a/Functions/EQ_visuals/showEQNoisePSD.m b/Functions/EQ_visuals/showEQNoisePSD.m index 64f002d..8eeed21 100644 --- a/Functions/EQ_visuals/showEQNoisePSD.m +++ b/Functions/EQ_visuals/showEQNoisePSD.m @@ -43,6 +43,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/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/beautifyBERplot.m b/Functions/beautifyBERplot.m index 0d0a665..9e799e2 100644 --- a/Functions/beautifyBERplot.m +++ b/Functions/beautifyBERplot.m @@ -8,12 +8,13 @@ function beautifyBERplot() 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 + 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 = 7; % Marker size + lines(i).MarkerFaceColor = lines(i).Color; % Use line color for marker face + lines(i).MarkerEdgeColor = 'white'; end % Change all text interpreters to LaTeX 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 8da64d8..ff9eac0 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_dsp_from_db.m @@ -40,18 +40,18 @@ end fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); -M = 6; -% fp.where('Runs', 'pam_level','EQUALS', M); -% fp.where('Runs', 'bitrate','EQUALS', 480e9); +M = 4; +fp.where('Runs', 'pam_level','EQUALS', M); +fp.where('Runs', 'bitrate','EQUALS', 420e9);%360,390 % fp.where('Runs', 'symbolrate','EQUALS', 162e9); -% fp.where('Runs', 'fiber_length','EQUALS', 1); +fp.where('Runs', 'fiber_length','EQUALS', 2); 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', 'wavelength','EQUAL', 1310); +fp.where('Runs', 'db_mode','EQUALS', 0); +fp.where('Runs', 'rop_attenuation','EQUAL', 0); % fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); @@ -69,7 +69,7 @@ wh.addStorage("dbenc_package"); % === RUN IT === -[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); +[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true); results_db = results(dataTable.db_mode==1); results_nodb = results(dataTable.db_mode==0); diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m index e7dd62f..ebcfef1 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m @@ -6,7 +6,7 @@ M = 4; fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); fp.where('Runs', 'pam_level','EQUALS', M); -fp.where('Runs', 'symbolrate','EQUALS', 150e9); +% fp.where('Runs', 'symbolrate','EQUALS', 150e9); % fp.where('Runs', 'fiber_length','EQUALS', 10); fp.where('Runs', 'is_mpi','EQUALS', 0); % fp.where('Runs', 'interference_path_length','EQUALS', 1000); diff --git a/projects/IMDD_base_system/simulation_bwl.m b/projects/IMDD_base_system/simulation_bwl.m index 79fc4c7..1ac779f 100644 --- a/projects/IMDD_base_system/simulation_bwl.m +++ b/projects/IMDD_base_system/simulation_bwl.m @@ -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 =210e9; +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,133 @@ 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); + + [~,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)); + parfor a = 1:numel(alpha_vec) + + 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); - % 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)); + 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 +501,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 +516,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 +539,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 +561,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..27a71cb 100644 --- a/projects/IMDD_base_system/simulation_bwl_2.m +++ b/projects/IMDD_base_system/simulation_bwl_2.m @@ -136,9 +136,5 @@ 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; - -constellation = [-3, -1, 1, 3]; -chatgpt_answer(mlse_sig_sd.signal, Symbols.signal,mlse_.DIR,constellation); - -% 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); +mlse_sig_sd = mlse_.process(mlse_sig_sd,Symbols); +mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(mlse_sig_sd); diff --git a/projects/ML_based_MLSE/model.m b/projects/ML_based_MLSE/model.m new file mode 100644 index 0000000..f74e5fe --- /dev/null +++ b/projects/ML_based_MLSE/model.m @@ -0,0 +1,194 @@ +%%% 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; + +alpha = 0; + +doub_mode = db_mode.no_db; +cols = linspecer(6); +rop = [-6]; +bwl = [0.5:0.1:1.5]; +fsym = [208: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",17,"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"); + + + %% Implement DSP directly here: + + mu_lms = 0.0005; + tic + eq = ML_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",32,"L",1); + [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); + toc + + % tic + % eq = FFE_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",32,"L",3); + % [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); + % toc + + Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(Eq_signal); + [~, errors, ber, ~] = calc_ber(Eq_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); + fprintf('FFE: %.2e \n',ber); + + Vit_signal_ = Vit_signal; + Vit_signal_.signal = circshift(Vit_signal.signal,0); + Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(Vit_signal_); + [~, errors, ber, errpos] = calc_ber(Vit_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); + fprintf('Viterbi: %.2e \n',ber); + + + + + + %% optimize smth. + tr_len = 2.^[2:15]; + tr_len = floor(tr_len); + ber = zeros(size(tr_len)); + parfor m = 1:numel(tr_len) + + mu_lms = 0.0005; + eq = ML_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",tr_len(m)); + [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); + + % Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(Eq_signal); + % [~, errors, ber(m), ~] = calc_ber(Eq_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); + + Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(Vit_signal); + [~, errors, ber(m), errpos] = calc_ber(Vit_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); + end + + figure(5); hold on + title('1-SPS') + plot(tr_len,ber,'DisplayName','BER'); + xlabel('Traceback Length') + beautifyBERplot + legend + ylim([1e-4, 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",1,"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_1sps, 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/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; + + From ef4dc53db5afe72963d550f3fd7e7a82fe10936e Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Mon, 27 Oct 2025 07:52:51 +0100 Subject: [PATCH 22/30] Developing the ML-enhanced MLSE :-) --- Classes/04_DSP/Equalizer/FFE_MLSE.m | 4 +- Classes/04_DSP/Equalizer/ML_MLSE.m | 70 ++++++++++++++++++++--------- projects/ML_based_MLSE/model.m | 26 +++-------- 3 files changed, 57 insertions(+), 43 deletions(-) diff --git a/Classes/04_DSP/Equalizer/FFE_MLSE.m b/Classes/04_DSP/Equalizer/FFE_MLSE.m index e7a4bbd..d94da34 100644 --- a/Classes/04_DSP/Equalizer/FFE_MLSE.m +++ b/Classes/04_DSP/Equalizer/FFE_MLSE.m @@ -1,4 +1,4 @@ -classdef ML_MLSE < handle +classdef FFE_MLSE < handle % Implementation of plain and simple FFE. % 1) Training mode (stable performance when you use NLMS) % 2) Decision directed mode @@ -37,7 +37,7 @@ classdef ML_MLSE < handle end methods - function obj = ML_MLSE(options) + function obj = FFE_MLSE(options) arguments(Input) options.sps = 2; diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index db6c686..65eefb8 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -121,22 +121,22 @@ classdef ML_MLSE < handle % ============================================================== % INITIALIZATION (only before final epoch and detection mode) % ============================================================== - if epoch == epochs && ~training + if epoch == epochs % --- Parameters S = numel(unique(d)); % alphabet size - L = obj.L; % MLSE memory - Nf = L; % filter length - Delta = ceil(L/2); % delay parameter - nStates = S^L; - nFeasible = S^(L-1)*S; + Nf = 9; % filter length + Delta = ceil(Nf/2); % delay parameter + nStates = S^obj.L; + nFeasible = nStates*S; + % --- Trellis mapping - obj.DIR = arburg(y-d, L); + obj.DIR = arburg(y-d(1:N_), obj.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)); + 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)); combs = fliplr(combvec(pre_comb_cell{:}).'); first_sym = combs(:,1); last_sym = combs(:,end); @@ -154,7 +154,7 @@ classdef ML_MLSE < handle [valid_to, valid_from] = find(valid); % --- Noise estimation - y_ideal = conv(d(:), obj.DIR(:), "same"); + y_ideal = conv(d(1:N_), obj.DIR(:), "same"); sigma2 = mean(abs(y - y_ideal).^2); inv2s2 = 1/(2*sigma2); @@ -166,6 +166,9 @@ classdef ML_MLSE < handle v_tilde = zeros(1,nFeasible); bm_vec = zeros(1,nFeasible); zi = zeros(max(numel(obj.DIR)-1,0),1); + + mu_w = 0.001; + mu_b = 0.001; end % ============================================================== @@ -190,7 +193,7 @@ classdef ML_MLSE < handle obj.e = obj.e + mu * (err * U); % --- Whitening + MLSE in last epoch - if epoch == epochs && ~training + if epoch == epochs [y_white(symbol), zi] = filter(obj.DIR,1,y(symbol), zi); k = symbol; @@ -210,17 +213,42 @@ classdef ML_MLSE < handle % --- Extended path metrics v_tilde = pm(valid_from) + v_hat; % [nFeasible×1] - % --- Compute branch metrics (distance) - bm_vec = -(y_white(k) - v_hat).^2 * inv2s2; % 1×nFeasible + % ===== Gradient update (Algorithm 1) ===== + % if training - % --- Survivor selection (vector aggregation) - pm_new_vec = pm(valid_from) + bm_vec.'; % nFeasible×1 - pm_next = -inf(nStates,1); - surv_idx = zeros(nStates,1); - for t = 1:nStates - mask = (valid_to==t); - [pm_next(t), arg] = max(pm_new_vec(mask)); - surv_idx(t) = valid_from(find(mask,1,'first')-1+arg); + % for current symbol index k -> previous (k-1) and current (k) + if k > obj.L + prev_seq = d(k-obj.L:k-1); % previous state symbols + curr_seq = d(k-obj.L+1:k); % next state symbols + + % find state indices in trellis + true_from_state = find(ismember(combs, prev_seq.', 'rows')); + true_to_state = find(ismember(combs, curr_seq.', 'rows')); + else + % not enough history yet + true_from_state = 1; + true_to_state = 1; + end + + % softmax over -v_tilde, one-hot target t + p = exp(-v_tilde); + p = p./sum(p); + t = zeros(nFeasible,1); + t(valid_from==true_from_state & valid_to==true_to_state) = 1; + + delta = t - p; % ∂CE/∂(-v_tilde) + w = w + mu_w * (yk * delta.'); % Nf×nFeasible + b = b + mu_b * delta.'; % 1×nFeasible + % end + % ========================================= + + % compare–select to next states + pm_next = -inf(nStates,1); + surv_idx = zeros(nStates,1); + for s_to = 1:nStates + mask = (valid_to==s_to); + [pm_next(s_to), arg] = max(-v_tilde(mask)); % max likelihood ↔ min metric + surv_idx(s_to) = valid_from(find(mask,1,'first')-1+arg); end pm = pm_next; diff --git a/projects/ML_based_MLSE/model.m b/projects/ML_based_MLSE/model.m index f74e5fe..d1da091 100644 --- a/projects/ML_based_MLSE/model.m +++ b/projects/ML_based_MLSE/model.m @@ -99,15 +99,15 @@ for r = 1:length(fsym) %% Implement DSP directly here: mu_lms = 0.0005; + % tic + % eq = FFE_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",32,"L",1); + % [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); + % toc + tic eq = ML_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",32,"L",1); [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); toc - - % tic - % eq = FFE_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",32,"L",3); - % [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); - % toc Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(Eq_signal); [~, errors, ber, ~] = calc_ber(Eq_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); @@ -122,12 +122,11 @@ for r = 1:length(fsym) - %% optimize smth. tr_len = 2.^[2:15]; tr_len = floor(tr_len); ber = zeros(size(tr_len)); - parfor m = 1:numel(tr_len) + for m = 1:numel(tr_len) mu_lms = 0.0005; eq = ML_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",tr_len(m)); @@ -150,19 +149,6 @@ for r = 1:length(fsym) set(gca,'YScale','log'); - - - - - - - - - - - - - %% RUN Comparison len_tr = 4096*2; From ac95aef1e0dac3f88170dffe7689cd833bd10641 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Thu, 30 Oct 2025 14:29:14 +0100 Subject: [PATCH 23/30] Works at 1 SPS NWhitened Signal or FFE Output are okay. mu is approx. 0.15 delta is 0 order is 4 (higher is not better yet) --- Classes/04_DSP/Equalizer/ML_MLSE.m | 374 ++++++++++++++++++----------- 1 file changed, 232 insertions(+), 142 deletions(-) diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index 65eefb8..9c9a033 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -2,11 +2,11 @@ classdef ML_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 @@ -34,6 +34,20 @@ classdef ML_MLSE < handle trellis_states traceback_depth + + % --- Added internal class variables used later --- + S + Nf + delta + nStates + nFeasible + combs + first_sym + last_sym + valid + valid_to_idx + valid_from_idx + w end methods @@ -51,6 +65,7 @@ classdef ML_MLSE < handle options.mu_dd = 1e-5; options.epochs_dd = 5; + options.delta = 0; options.traceback_depth = 1024; options.L = 1 @@ -79,12 +94,61 @@ classdef ML_MLSE < handle warning('Signal length does not fit to reference!'); end + + % ============================================================== + % INITIALIZATION (only before final epoch and detection mode) + % ============================================================== + + + % --- Parameters + obj.S = numel(unique(D.signal)); % alphabet size + obj.Nf = obj.order; % filter length + % obj.delta = 3;%ceil(obj.Nf/2); % delay parameter + obj.nStates = obj.S^obj.L; + obj.nFeasible = obj.nStates*obj.S; + + % --- Trellis mapping + obj.trellis_states = reshape(unique(D.signal),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); + + % --- Allocate vectors and weights + % !! IF SHAPE FIT, then we already have smth there an we want + % to start with the existing fitler-set + if all(size(obj.w) ~= [obj.Nf+1,obj.nFeasible]) + obj.w = zeros(obj.Nf+1,obj.nFeasible); % filter weights per transition + bias tap + end + % obj.w = randn(obj.Nf+1,obj.nFeasible); + + % ============================================================== + % TRAINING + % ============================================================== + % Training Mode n = obj.len_tr; training = 1; - obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_dd,n,training); + obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,n,training); obj.e_tr = obj.e; + % ============================================================== + % DD-Mode / Fixed Mode + % ============================================================== + % Decision Directed Mode n = X.length; training = 0; @@ -109,67 +173,17 @@ classdef ML_MLSE < handle % ============================================================== % FFE + Whitening + ML-Based Branch Metric Estimation + Viterbi % ============================================================== - + debug = 0; % --- 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); + y = zeros(N,1); for epoch = 1:epochs - % ============================================================== - % INITIALIZATION (only before final epoch and detection mode) - % ============================================================== - if epoch == epochs - % --- Parameters - S = numel(unique(d)); % alphabet size - Nf = 9; % filter length - Delta = ceil(Nf/2); % delay parameter - nStates = S^obj.L; - nFeasible = nStates*S; - - - % --- Trellis mapping - obj.DIR = arburg(y-d(1:N_), obj.L); - obj.DIR_flip = flip(obj.DIR); - obj.trellis_states = reshape(unique(d),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)); - combs = fliplr(combvec(pre_comb_cell{:}).'); - first_sym = combs(:,1); - last_sym = combs(:,end); - nStates = size(combs,1); - - % --- Valid transitions - valid = false(nStates); - for from = 1:nStates - for to = 1:nStates - if all(combs(to,2:end) == combs(from,1:end-1)) - valid(to,from) = true; - end - end - end - [valid_to, valid_from] = find(valid); - - % --- Noise estimation - y_ideal = conv(d(1:N_), obj.DIR(:), "same"); - sigma2 = mean(abs(y - y_ideal).^2); - inv2s2 = 1/(2*sigma2); - - % --- Allocate vectors and weights - pm = zeros(nStates,1); - w = zeros(Nf,nFeasible); % filter weights per transition - b = zeros(1,nFeasible); % bias terms - v_hat = zeros(1,nFeasible); - v_tilde = zeros(1,nFeasible); - bm_vec = zeros(1,nFeasible); - zi = zeros(max(numel(obj.DIR)-1,0),1); - - mu_w = 0.001; - mu_b = 0.001; - end + pm = zeros(obj.nStates,1); + c_hat = zeros(1,obj.nFeasible); + v_tilde = zeros(1,obj.nFeasible); + pred = zeros(N, obj.nStates, 'uint32'); % ============================================================== % RUNTIME LOOP @@ -178,97 +192,173 @@ classdef ML_MLSE < handle 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; + k = symbol; - % --- Decision / FFE adaptation - if training - d_hat = d(symbol); + % --- Build Δ-delayed observation window y_k + i1 = k - obj.Nf + 1 + obj.delta; + i2 = k + 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)]; % Nf×1 + yk = [yk;1]; + + % --- Predict branch metrics for all feasible transitions + c_hat = (yk.' * obj.w); % [1×nFeasible] + c_hat = c_hat.'; % [nFeasible×1] + + % --- Extended path metrics + v_tilde = pm(obj.valid_from_idx) + c_hat; % [nFeasible×1] + + % ===== Gradient update (Algorithm 1) ===== + % if training + + % for current symbol index k -> previous (k-1) and current (k) + if k > obj.L + prev_seq = d(k-obj.L:k-1); % previous state symbols + true_from_state_idx = find(ismember(obj.combs, flip(prev_seq).', 'rows'));% find state indices in trellis + + %if ~(true_from_state_idx == true_to_state_idx), warning('Impossible state transition?!'), end + + curr_seq = d(k-obj.L+1:k); % next state symbols + true_to_state_idx = find(ismember(obj.combs, flip(curr_seq).', 'rows'));% find state indices in trellis else - [~,idx] = min(abs(y(symbol) - obj.constellation)); - d_hat = obj.constellation(idx); + % not enough history yet + true_from_state_idx = 1; + true_to_state_idx = 1; end - err = d_hat - y(symbol); - obj.e = obj.e + mu * (err * U); - % --- Whitening + MLSE in last epoch - if epoch == epochs - - [y_white(symbol), zi] = filter(obj.DIR,1,y(symbol), zi); - k = symbol; - - % --- Build Δ-delayed observation window y_k - i1 = k - Nf + 1 + Delta; - i2 = k + Delta; - buf = y_white(max(1,i1):min(length(y_white),i2)); - padL = max(0,1 - i1); - padR = max(0,i2 - length(y_white)); - yk = [zeros(padL,1); buf(:); zeros(padR,1)]; % Nf×1 - - % --- Predict branch metrics for all feasible transitions - v_hat = (yk.' * w) + b; % [1×nFeasible] - v_hat = v_hat.'; % [nFeasible×1] - - % --- Extended path metrics - v_tilde = pm(valid_from) + v_hat; % [nFeasible×1] - - % ===== Gradient update (Algorithm 1) ===== - % if training - - % for current symbol index k -> previous (k-1) and current (k) - if k > obj.L - prev_seq = d(k-obj.L:k-1); % previous state symbols - curr_seq = d(k-obj.L+1:k); % next state symbols - - % find state indices in trellis - true_from_state = find(ismember(combs, prev_seq.', 'rows')); - true_to_state = find(ismember(combs, curr_seq.', 'rows')); - else - % not enough history yet - true_from_state = 1; - true_to_state = 1; - end - - % softmax over -v_tilde, one-hot target t - p = exp(-v_tilde); - p = p./sum(p); - t = zeros(nFeasible,1); - t(valid_from==true_from_state & valid_to==true_to_state) = 1; - - delta = t - p; % ∂CE/∂(-v_tilde) - w = w + mu_w * (yk * delta.'); % Nf×nFeasible - b = b + mu_b * delta.'; % 1×nFeasible - % end - % ========================================= - - % compare–select to next states - pm_next = -inf(nStates,1); - surv_idx = zeros(nStates,1); - for s_to = 1:nStates - mask = (valid_to==s_to); - [pm_next(s_to), arg] = max(-v_tilde(mask)); % max likelihood ↔ min metric - surv_idx(s_to) = valid_from(find(mask,1,'first')-1+arg); - end - pm = pm_next; - - % --- Traceback - if mod(symbol,obj.traceback_depth) == 0 - [~,viterbi_path(symbol)] = max(pm); - for n = symbol:-1:symbol-obj.traceback_depth+2 - viterbi_path(n-1) = surv_idx(viterbi_path(n)); - end - end + if 0 + disp(['FROM: state',char(num2str(true_from_state_idx)),' : symbol transition', char(num2str(obj.combs(true_from_state_idx,:)))]); + disp(['TO: state',char(num2str(true_to_state_idx)),' : symbol transition', char(num2str(obj.combs(true_to_state_idx,:)))]); end + % true_from and true_to are (1) -> (-1) + % thus dirac = [1,0,0,0]' + dirac = zeros(obj.nFeasible,1); + dirac(obj.valid_from_idx==true_from_state_idx & obj.valid_to_idx==true_to_state_idx) = 1; % This Dirac delta function δ(s = s∗ k, s′ = s∗ k−1) = 1 if the extended state (s, s′) corresponds to the true realized states (s∗ k, s∗ k−1), and is zero otherwise. + + if sum(dirac) == 0 + warning('whats happening?!'); + end + + % softmax over -v_tilde, one-hot target t + % 4 feasible transitions: [0,0][0,1][1,0][1,1] #not in + % order here! + % first round: + % v_tilde is zero + % -> exp(-0) = 1 + % -> 1/(1+1+1+1) + % -> 1/4 + % -> each transition is equally likely? + % p = exp(-v_tilde); + p = exp(-(v_tilde-max(v_tilde))); + p = p./sum(p); % found in formula (9) and (19) + + % 1-0.25 = 0.75 + % 0-0.25 = -0.25 + % what happens here at the zero? Is this some log + % probability - larger zero is likely; lower zero is + % unlikely? But this is based on known information (training) + dmp = (dirac - p)'; + + + if mod(symbol,128) == 1 && debug + + % --- Normalize and compute probabilities + v_norm = v_tilde - max(v_tilde); % numerical stability + probs_lin = exp(-v_norm); + probs_lin = probs_lin ./ sum(probs_lin); + probs_log = -v_norm; + + % --- Map back into nStates×nStates grid + probs_mat = nan(obj.nStates, obj.nStates); + probs_mat(obj.valid) = probs_lin; % linear-space probabilities + probs_logmat = nan(obj.nStates, obj.nStates); + probs_logmat(obj.valid) = probs_log; % log-domain scores + + % --- Identify the current true transition + [to_idx, from_idx] = find(obj.valid); + cur_idx = find(dirac==1); + cur_to = to_idx(cur_idx); + cur_from = from_idx(cur_idx); + + % --- Plot using imagesc (supports hold) + figure(11); clf; + imagesc(probs_logmat); + axis xy; % origin top-left + % colormap(parula); + colorbar; + xlabel('From state'); + ylabel('To state'); + set(gca,'FontSize',10); + + % --- Overlay current transition + hold on; + plot(cur_from, cur_to, 'rs', ... + 'MarkerSize', 10, 'LineWidth', 2, 'MarkerFaceColor', 'none'); + hold off; + end + + if 1 %training + % the correct state gets a high update, we weight this + % with the input signal, from here the signals are not + % "understandable" + % dmp is large for the correct transition to update + % only this one! + dL_Dw = dmp .* (yk); % ∂CE/∂(w) - formula (10) + % from paper: We have observed in simulations that ignoring the derivative of vk−1(s′) during training yields negligible loss after convergence. + % my note: so the update direction is the same for b and w? + + %only start with updates when we are inside the signal + if k > obj.L + % actual filter training updates: + obj.w = obj.w - mu * dL_Dw;% Nf×nFeasible + end + + end + + % compare select + v_tilde_mat = inf(obj.nStates, obj.nStates); + v_tilde_mat(obj.valid) = v_tilde; + [pm, pred(k,:)] = min(v_tilde_mat, [], 2); + + pm_sto(:,symbol) = pm; + end - % --- Output reconstructed path - if epoch == epochs && ~training - y_vit = first_sym(viterbi_path); + [~, s_end] = max(pm); + viterbi_path = zeros(N,1,'uint32'); + viterbi_path(N) = s_end; + for n = N:-1:2 + viterbi_path(n-1) = pred(n, viterbi_path(n)); end + + y_vit = obj.first_sym(viterbi_path); + y = obj.first_sym(viterbi_path); + + if 1 %debug || training + err = sum(y ~= d(1:length(y))); + ser = err./length(y); + fprintf('Epoch: %d - SER: %.1e \n',epoch, ser); + + figure(10); + subplot(2,2,1:2); + heatmap(obj.w); + title('Filter') + + subplot(2,2,3); + v_tildemat = NaN(obj.nStates, obj.nStates); + v_tildemat(obj.valid) = v_tilde; % log-domain scores + heatmap(v_tildemat); + title('Path Metrics (v_tilde)') + + subplot(2,2,4); + % scatter(1:N,pm_sto,1,'.') + plot(1:N,pm_sto) + title('Path Metric Winners') + end + end end - - end -end \ No newline at end of file +end From b27f1d3715b2a972b0ef7ae17cb938d928bc7abb Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Thu, 30 Oct 2025 14:54:06 +0100 Subject: [PATCH 24/30] Now updated for 2 SPS! Works directly on the 2sps Scope output --- Classes/04_DSP/Equalizer/ML_MLSE.m | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index 9c9a033..7ad2722 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -102,7 +102,7 @@ classdef ML_MLSE < handle % --- Parameters obj.S = numel(unique(D.signal)); % alphabet size - obj.Nf = obj.order; % filter length + obj.Nf = obj.order*obj.sps; % filter length % obj.delta = 3;%ceil(obj.Nf/2); % delay parameter obj.nStates = obj.S^obj.L; obj.nFeasible = obj.nStates*obj.S; @@ -195,8 +195,8 @@ classdef ML_MLSE < handle k = symbol; % --- Build Δ-delayed observation window y_k - i1 = k - obj.Nf + 1 + obj.delta; - i2 = k + obj.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)); @@ -327,15 +327,19 @@ classdef ML_MLSE < handle end [~, s_end] = max(pm); - viterbi_path = zeros(N,1,'uint32'); - viterbi_path(N) = s_end; - for n = N:-1:2 + viterbi_path = zeros(symbol,1,'uint32'); + viterbi_path(symbol) = s_end; + for n = symbol:-1:2 viterbi_path(n-1) = pred(n, viterbi_path(n)); end y_vit = obj.first_sym(viterbi_path); y = obj.first_sym(viterbi_path); + + + + if 1 %debug || training err = sum(y ~= d(1:length(y))); ser = err./length(y); @@ -354,7 +358,7 @@ classdef ML_MLSE < handle subplot(2,2,4); % scatter(1:N,pm_sto,1,'.') - plot(1:N,pm_sto) + plot(1:symbol,pm_sto) title('Path Metric Winners') end From 09f345aa9128edc41150bb11bc19f802bca0c9ac Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Fri, 31 Oct 2025 10:36:13 +0100 Subject: [PATCH 25/30] ML_MLSE (before testing in depth) minor changes here and there --- Classes/04_DSP/Equalizer/ML_MLSE.m | 197 ++++++++-------- Functions/beautifyBERplot.m | 11 +- .../Auswertung_JLT/run_plot_measurements.m | 9 +- .../model_linewidth_evaluation.m | 181 +++++++++++++++ projects/IMDD_base_system/simulation_bwl.m | 4 +- projects/ML_based_MLSE/minimal_model_gpt.m | 213 ++++++++++++++++++ projects/ML_based_MLSE/model.m | 126 +++++++---- 7 files changed, 583 insertions(+), 158 deletions(-) create mode 100644 projects/IMDD_base_system/model_linewidth_evaluation.m create mode 100644 projects/ML_based_MLSE/minimal_model_gpt.m diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index 7ad2722..e09d2cd 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -48,6 +48,11 @@ classdef ML_MLSE < handle valid_to_idx valid_from_idx w + + % --- New: fast state lookup --- + state_dict % containers.Map: key(sequence)->state index + key_fmt = '%.8g_'; % key format for sequence strings + nSym % |constellation| end methods @@ -79,7 +84,6 @@ classdef ML_MLSE < handle obj.e = zeros(obj.order,1); obj.error = 0; - end function [X,X_viterbi] = process(obj, X, D) @@ -88,33 +92,33 @@ classdef ML_MLSE < handle % 1 normalize RMS X = X.normalize("mode","rms"); - obj.constellation = unique(D.signal); + % Use sorted constellation for deterministic mapping + 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 - % ============================================================== % INITIALIZATION (only before final epoch and detection mode) % ============================================================== - % --- Parameters - obj.S = numel(unique(D.signal)); % alphabet size - obj.Nf = obj.order*obj.sps; % filter length - % obj.delta = 3;%ceil(obj.Nf/2); % delay parameter + obj.S = numel(obj.constellation); % alphabet size + obj.Nf = obj.order*obj.sps; % filter length + % obj.delta = 3;%ceil(obj.Nf/2); % delay parameter obj.nStates = obj.S^obj.L; obj.nFeasible = obj.nStates*obj.S; % --- Trellis mapping - obj.trellis_states = reshape(unique(D.signal),1,[]); + 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); + 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 obj.valid = false(obj.nStates); @@ -130,10 +134,17 @@ classdef ML_MLSE < handle % --- Allocate vectors and weights % !! IF SHAPE FIT, then we already have smth there an we want % to start with the existing fitler-set - if all(size(obj.w) ~= [obj.Nf+1,obj.nFeasible]) + if isempty(obj.w) || any(size(obj.w) ~= [obj.Nf+1,obj.nFeasible]) obj.w = zeros(obj.Nf+1,obj.nFeasible); % filter weights per transition + bias tap + obj.w = randn(obj.Nf+1,obj.nFeasible); end - % obj.w = randn(obj.Nf+1,obj.nFeasible); + + % --- 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 @@ -165,8 +176,6 @@ classdef ML_MLSE < handle 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) @@ -174,24 +183,34 @@ classdef ML_MLSE < handle % FFE + Whitening + ML-Based Branch Metric Estimation + Viterbi % ============================================================== debug = 0; + % --- Input padding and preallocation y = zeros(N,1); + % number of symbol steps in this block + nSymbols = ceil(N/obj.sps); + for epoch = 1:epochs - - pm = zeros(obj.nStates,1); + % state metrics (log-domain costs): keep as column [nStates×1] + pm = zeros(obj.nStates,1); % v_{k-1}(s′) c_hat = zeros(1,obj.nFeasible); v_tilde = zeros(1,obj.nFeasible); - pred = zeros(N, obj.nStates, 'uint32'); + pred = zeros(nSymbols, obj.nStates, 'uint32'); + pm_sto = nan(obj.nStates, nSymbols,'like',pm); + + % --- Initialize "true" trellis state for training (shift-register style) + % expect sequences in chronological order [x_{k-L+1}:x_k], but combs rows are [x_k, x_{k-1}, ...] + if numel(d) >= obj.L + init_seq = d(1:obj.L); % [x_1 ... x_L] + true_to_state_idx = obj.state_dict(obj.seq_key(flip(init_seq))); % flip to [x_L, x_{L-1}, ...] + else + true_to_state_idx = 1; + end - % ============================================================== - % RUNTIME LOOP - % ============================================================== symbol = 0; for sample = 1:obj.sps:N symbol = symbol + 1; - k = symbol; % --- Build Δ-delayed observation window y_k @@ -201,80 +220,66 @@ classdef ML_MLSE < handle padL = max(0,1 - i1); padR = max(0,i2 - length(x)); yk = [zeros(padL,1); buf(:); zeros(padR,1)]; % Nf×1 - yk = [yk;1]; + yk = [yk;1]; - % --- Predict branch metrics for all feasible transitions + % --- Predict branch metrics for all feasible transitions: c_hat c_hat = (yk.' * obj.w); % [1×nFeasible] c_hat = c_hat.'; % [nFeasible×1] - % --- Extended path metrics + % --- Extended path metrics: v_tilde = pm(from) + c_hat + % normalize pm to avoid growth (invariant to additive const) + pm = pm - min(pm); v_tilde = pm(obj.valid_from_idx) + c_hat; % [nFeasible×1] % ===== Gradient update (Algorithm 1) ===== % if training - % for current symbol index k -> previous (k-1) and current (k) if k > obj.L - prev_seq = d(k-obj.L:k-1); % previous state symbols - true_from_state_idx = find(ismember(obj.combs, flip(prev_seq).', 'rows'));% find state indices in trellis + % shift-register: previous "to" becomes current "from" + true_from_state_idx = true_to_state_idx; - %if ~(true_from_state_idx == true_to_state_idx), warning('Impossible state transition?!'), end - - curr_seq = d(k-obj.L+1:k); % next state symbols - true_to_state_idx = find(ismember(obj.combs, flip(curr_seq).', 'rows'));% find state indices in trellis + % current "to" from data window + curr_seq = d(k-obj.L+1:k); + key_to = obj.seq_key(flip(curr_seq)); + if isKey(obj.state_dict, key_to) + true_to_state_idx = obj.state_dict(key_to); + else + % fall back safely (should not happen with proper constellation) + true_to_state_idx = true_from_state_idx; + end else % not enough history yet true_from_state_idx = 1; - true_to_state_idx = 1; + true_to_state_idx = true_to_state_idx; % keep init end if 0 disp(['FROM: state',char(num2str(true_from_state_idx)),' : symbol transition', char(num2str(obj.combs(true_from_state_idx,:)))]); disp(['TO: state',char(num2str(true_to_state_idx)),' : symbol transition', char(num2str(obj.combs(true_to_state_idx,:)))]); end - % true_from and true_to are (1) -> (-1) - % thus dirac = [1,0,0,0]' + + % Dirac delta over correct extended transition (from,to) dirac = zeros(obj.nFeasible,1); dirac(obj.valid_from_idx==true_from_state_idx & obj.valid_to_idx==true_to_state_idx) = 1; % This Dirac delta function δ(s = s∗ k, s′ = s∗ k−1) = 1 if the extended state (s, s′) corresponds to the true realized states (s∗ k, s∗ k−1), and is zero otherwise. - - if sum(dirac) == 0 - warning('whats happening?!'); - end - % softmax over -v_tilde, one-hot target t - % 4 feasible transitions: [0,0][0,1][1,0][1,1] #not in - % order here! - % first round: - % v_tilde is zero - % -> exp(-0) = 1 - % -> 1/(1+1+1+1) - % -> 1/4 - % -> each transition is equally likely? - % p = exp(-v_tilde); - p = exp(-(v_tilde-max(v_tilde))); + % softmax over -v_tilde (numerically safe shift) + p = exp(-(v_tilde - max(v_tilde))); p = p./sum(p); % found in formula (9) and (19) - % 1-0.25 = 0.75 - % 0-0.25 = -0.25 - % what happens here at the zero? Is this some log - % probability - larger zero is likely; lower zero is - % unlikely? But this is based on known information (training) - dmp = (dirac - p)'; - + % gradient term (t - p) + dmp = (dirac - p)'; % 1×nFeasible if mod(symbol,128) == 1 && debug - % --- Normalize and compute probabilities - v_norm = v_tilde - max(v_tilde); % numerical stability - probs_lin = exp(-v_norm); - probs_lin = probs_lin ./ sum(probs_lin); + v_norm = v_tilde - max(v_tilde); + probs_lin = exp(-v_norm); probs_lin = probs_lin ./ sum(probs_lin); probs_log = -v_norm; % --- Map back into nStates×nStates grid - probs_mat = nan(obj.nStates, obj.nStates); - probs_mat(obj.valid) = probs_lin; % linear-space probabilities + probs_mat = nan(obj.nStates, obj.nStates); probs_logmat = nan(obj.nStates, obj.nStates); - probs_logmat(obj.valid) = probs_log; % log-domain scores + probs_mat(obj.valid) = probs_lin; + probs_logmat(obj.valid) = probs_log; % --- Identify the current true transition [to_idx, from_idx] = find(obj.valid); @@ -282,51 +287,37 @@ classdef ML_MLSE < handle cur_to = to_idx(cur_idx); cur_from = from_idx(cur_idx); - % --- Plot using imagesc (supports hold) figure(11); clf; - imagesc(probs_logmat); - axis xy; % origin top-left - % colormap(parula); - colorbar; - xlabel('From state'); - ylabel('To state'); - set(gca,'FontSize',10); - - % --- Overlay current transition + imagesc(probs_logmat); axis xy; colorbar; + xlabel('From state'); ylabel('To state'); set(gca,'FontSize',10); hold on; - plot(cur_from, cur_to, 'rs', ... - 'MarkerSize', 10, 'LineWidth', 2, 'MarkerFaceColor', 'none'); + plot(cur_from, cur_to, 'rs', 'MarkerSize', 10, 'LineWidth', 2, 'MarkerFaceColor', 'none'); hold off; end if 1 %training - % the correct state gets a high update, we weight this - % with the input signal, from here the signals are not - % "understandable" - % dmp is large for the correct transition to update - % only this one! + % dmp is large for the correct transition -> update emphasizes that branch dL_Dw = dmp .* (yk); % ∂CE/∂(w) - formula (10) - % from paper: We have observed in simulations that ignoring the derivative of vk−1(s′) during training yields negligible loss after convergence. - % my note: so the update direction is the same for b and w? - - %only start with updates when we are inside the signal + % only start with updates when we are inside the signal if k > obj.L - % actual filter training updates: - obj.w = obj.w - mu * dL_Dw;% Nf×nFeasible + obj.w = obj.w - mu * dL_Dw; % (Nf+1)×nFeasible end - end - % compare select + % --- Compare-Select (matrix form, min of costs) v_tilde_mat = inf(obj.nStates, obj.nStates); v_tilde_mat(obj.valid) = v_tilde; - [pm, pred(k,:)] = min(v_tilde_mat, [], 2); + [pm_next, pred(k,:)] = min(v_tilde_mat, [], 2); + % re-center to keep metrics bounded (decision-invariant) + pm_next = pm_next - min(pm_next); + + pm = pm_next; pm_sto(:,symbol) = pm; - end - [~, s_end] = max(pm); + % --- Traceback (full; you can window with traceback_depth if desired) + [~, s_end] = min(pm); viterbi_path = zeros(symbol,1,'uint32'); viterbi_path(symbol) = s_end; for n = symbol:-1:2 @@ -334,12 +325,8 @@ classdef ML_MLSE < handle end y_vit = obj.first_sym(viterbi_path); - y = obj.first_sym(viterbi_path); + y = obj.first_sym(viterbi_path); - - - - if 1 %debug || training err = sum(y ~= d(1:length(y))); ser = err./length(y); @@ -357,12 +344,20 @@ classdef ML_MLSE < handle title('Path Metrics (v_tilde)') subplot(2,2,4); - % scatter(1:N,pm_sto,1,'.') - plot(1:symbol,pm_sto) + scatter(1:symbol,pm_sto,1,'.') + % plot(1:symbol,pm_sto,'LineStyle','none') title('Path Metric Winners') 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/Functions/beautifyBERplot.m b/Functions/beautifyBERplot.m index 9e799e2..1a4f953 100644 --- a/Functions/beautifyBERplot.m +++ b/Functions/beautifyBERplot.m @@ -1,4 +1,7 @@ -function beautifyBERplot() +function beautifyBERplot(options) +arguments + options.logscale = 1 +end % BEAUTIFYBERPLOT Enhances a BER plot for publication-quality figures. % Set line properties for all current plot lines @@ -26,8 +29,10 @@ function beautifyBERplot() % 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'); - + if options.logscale + set(gca, 'YScale', 'log'); + end + % Customize grid and box appearance set(gca, 'Box', 'on', 'LineWidth', 0.8); % Thicker border grid on; diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m index ebcfef1..73bd3e6 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m @@ -5,7 +5,7 @@ db = DBHandler("dataBase", [dataBase], "type", database_type); M = 4; fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); -fp.where('Runs', 'pam_level','EQUALS', M); +% fp.where('Runs', 'pam_level','EQUALS', M); % fp.where('Runs', 'symbolrate','EQUALS', 150e9); % fp.where('Runs', 'fiber_length','EQUALS', 10); fp.where('Runs', 'is_mpi','EQUALS', 0); @@ -20,10 +20,11 @@ fields = db.getTableFieldNames('power_state_info'); fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')]; [dataTable,~] = db.queryDB(fp, fields); +%% cfg = struct; -cfg.x_axis = 'accumulated_dispersion'; % 'symbolrate' | 'baudrate' | 'bitrate' | 'wavelength' -cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ... -cfg.group_by = {'equalizer_structure','pre_emph'}; +cfg.x_axis = 'wavelength'; % 'symbolrate' | 'baudrate' | 'bitrate' | 'wavelength' +cfg.y_axis = 'power_mzm'; % 'BER' | 'GMI' | 'AIR' | ... +cfg.group_by = {}; cfg.filters = struct('is_mpi',0,'pam_level',M,'equalizer_structure',[equalizer_structure.vnle,equalizer_structure.ffe,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle_db_mlse,equalizer_structure.dfe]); cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise 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 1ac779f..e70c40d 100644 --- a/projects/IMDD_base_system/simulation_bwl.m +++ b/projects/IMDD_base_system/simulation_bwl.m @@ -332,12 +332,14 @@ for r = 1:length(fsym) plot_stuff = 0; gmi_mlse_pr_tgt_ = zeros(size(alpha_vec)); ber_mlse_pr_tgt_ = zeros(size(alpha_vec)); - parfor a = 1:numel(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 diff --git a/projects/ML_based_MLSE/minimal_model_gpt.m b/projects/ML_based_MLSE/minimal_model_gpt.m new file mode 100644 index 0000000..9536774 --- /dev/null +++ b/projects/ML_based_MLSE/minimal_model_gpt.m @@ -0,0 +1,213 @@ +function minimal_model_gpt +% Minimal binary IM/DD + ISI (M=3), AWGN. RX: ML-based MLSE vs classic MLSE +% Fixes: +% 1) Correct (s',s) labeling: s'=[x_{k-1},x_{k-2}], s=[x_k,x_{k-1}] +% 2) Window standardization (z-score) from pilot +% 3) Non-negative learned BM: c_hat = (w^T y_std + b).^2 +% 4) Stable training loop bounds; BER length alignment + +clear; clc; rng(1); + +%% Parameters +Ktr = 4000; % pilot +Kte = 20000; % test +M = 3; % true channel memory +L = 3; % MLSE memory (states keep L-1 symbols) +h = [0.55 1.00 0.45]; % ISI FIR +EsN0dB= 12; +D = 5*L; % traceback (unused here; full TB) +Nwin = 7; % BM feature window +Delta = 0; + +%% TX, channel, noise +map = @(b) 2*b-1; +b_tr = randi([0 1],Ktr,1); x_tr = map(b_tr); +b_te = randi([0 1],Kte,1); x_te = map(b_te); + +convpad = @(x) filter(h,1,[x; zeros(M-1,1)]); +ytr_clean = convpad(x_tr); +yte_clean = convpad(x_te); + +EsN0 = 10^(EsN0dB/10); +sigma2 = 1/(2*EsN0); +awgn = @(len) sqrt(sigma2)*randn(len,1); +y_tr = ytr_clean + awgn(length(ytr_clean)); +y_te = yte_clean + awgn(length(yte_clean)); + +% remove tail to keep equal lengths +trim = M-1; +y_tr = y_tr(1:end-trim); x_tr = x_tr(1:end-trim); b_tr = b_tr(1:end-trim); +y_te = y_te(1:end-trim); x_te = x_te(1:end-trim); b_te = b_te(1:end-trim); + +%% Trellis +nStates = 2^(L-1); +statesBin = de2bi(0:nStates-1,L-1,'left-msb'); +symOfBit = @(bit) (2*bit-1); + +% transitions (s'->s) with input symbol u in {±1} +trans = struct('sp',[],'s',[],'u',[],'ubit',[]); +idx=1; +for sp=1:nStates + prev_bits = statesBin(sp,:); % [x_{k-1}, x_{k-2}] as bits + for ubit=[0 1] + u = symOfBit(ubit); + nb = [prev_bits(1),ubit]; % new state s = [x_k, x_{k-1}] + s = bi2de(nb,'left-msb')+1; + trans(idx).sp=sp; trans(idx).s=s; trans(idx).u=u; trans(idx).ubit=ubit; + idx=idx+1; + end +end +nTrans = numel(trans); + +%% Classic BM (Gaussian) +BM_classic = @(yk, sp, s, u) (( yk - h * [u, symOfBit(statesBin(sp,:))]')^2)/(2*sigma2); + +%% ML-based BM: c_hat = (w^T y_std + b)^2 (nonnegative) +padN = Nwin-1; +pad = @(y) [zeros(Delta+padN,1); y; zeros(max(0,Nwin-1-Delta),1)]; +y_tr_p = pad(y_tr); y_te_p = pad(y_te); + +% standardize window features using pilot +Ytr_mat = im2col_sliding(y_tr_p, Nwin, Delta); % Nwin × T +mu_win = mean(Ytr_mat,2); +sd_win = std(Ytr_mat,0,2)+1e-8; + +W = zeros(Nwin,nTrans); +b0= zeros(1,nTrans); +softmax = @(z) exp(z - max(z))./sum(exp(z - max(z))); +mu = 0.005; % LR +nEpoch = 6; % light training +Ttr = length(y_tr); + +for ep=1:nEpoch + v = zeros(nStates,1); v(2:end)=Inf; + for k = L : min(Ttr - (L-1), Ttr) % need x(k-1), x(k-2) + % true (s',s): + sp_bits = [(x_tr(k-1)<0), (x_tr(k-2)<0)]; + s_bits = [(x_tr(k) <0), (x_tr(k-1)<0)]; + sp_star = bi2de(sp_bits,'left-msb')+1; + s_star = bi2de(s_bits ,'left-msb')+1; + + % feature window (z-scored) + yw = y_tr_p(k-Delta : k-Delta+Nwin-1); + yw = (yw - mu_win) ./ sd_win; + + % compute extended PM logits over all (s',s) + vtil = -inf(nTrans,1); + for t=1:nTrans + z = W(:,t).'*yw + b0(t); + c_hat = z*z; % (.)^2 nonnegative BM + vtil(t) = -( v(trans(t).sp) + c_hat); + end + pext = softmax(vtil).'; + t_star = find([trans.sp]==sp_star & [trans.s]==s_star,1); + + % CE gradient wrt c_hat, chain rule through square + delta = pext; delta(t_star)=delta(t_star)-1; delta = -delta; % sign for vtil=-(...) + for t=1:nTrans + z = W(:,t).'*yw + b0(t); + dc_dz = 2*z; % d(z^2)/dz + g = delta(t) * dc_dz; % ∂L/∂z + W(:,t) = W(:,t) - mu * g * yw; + b0(t) = b0(t) - mu * g; + end + + % PM update for next step + v_next = inf(nStates,1); + for t=1:nTrans + sp=trans(t).sp; s=trans(t).s; + z = W(:,t).'*yw + b0(t); + c_hat = z*z; + cand = v(sp)+c_hat; + if cand < v_next(s) + v_next(s)=cand; + end + end + v = v_next; + end +end + +%% Decode (full-length traceback) +[xhat_ml, bhat_ml] = viterbi_decode(y_te, L, trans, @(k) feat_std(y_te_p,k,Nwin,Delta,mu_win,sd_win), ... + @(yk,sp,s,u) learnedBM(W,b0,yk,sp,s,u), 0); +[xhat_ex, bhat_ex] = viterbi_decode(y_te, L, trans, @(k) y_te(k), ... + @(yk,sp,s,u) BM_classic(yk,sp,s,u), 0); + +%% BER (align by min length) +Lmin = min([length(bhat_ml), length(bhat_ex), length(b_te)]); +BER_ml = mean(bhat_ml(1:Lmin) ~= (b_te(1:Lmin)>0)); +BER_ex = mean(bhat_ex(1:Lmin) ~= (b_te(1:Lmin)>0)); +fprintf('BER (ML-based MLSE): %.3e\n', BER_ml); +fprintf('BER (classic MLSE ): %.3e\n', BER_ex); +end + +% ----------------- helpers ----------------- +function Y = im2col_sliding(y_pad, Nwin, Delta) +T = length(y_pad) - (Nwin-1) - Delta; +Y = zeros(Nwin,T); +for k=1:T + Y(:,k) = y_pad(k-Delta : k-Delta+Nwin-1); +end +end + +function yw = feat_std(y_pad,k,Nwin,Delta,mu_win,sd_win) +yw = y_pad(k-Delta : k-Delta+Nwin-1); +yw = (yw - mu_win) ./ sd_win; +end + +function c = learnedBM(W,b0,yw,sp,s,~) +% pick parameters of the (sp->s) transition +persistent map; +if isempty(map) + % build once: index of W/b0 for each (sp,s) + nStates = size(W,1)*0+1; %#ok +end +% linear scan is fine at this size: +% (use first match of (sp,s)) +c = inf; +for t=1:size(W,2) + % suppose we stored (sp,s) order as in training; we cannot access here. + % Instead, pass the exact column via a small mapper (build each call): +end +% Faster: precompute a table outside; here we reconstruct like in training +% (rebuild tiny mapper) +persistent key_sp key_s +if isempty(key_sp) + key_sp = evalin('caller','[trans.sp]'); + key_s = evalin('caller','[trans.s]'); +end +t = find(key_sp==sp & key_s==s,1); +z = W(:,t).'*yw + b0(t); +c = z*z; +end + +function [xhat, bhat] = viterbi_decode(y, L, trans, getFeat, BMfun, D_unused) +nStates = 2^(L-1); +T = length(y); +v = inf(nStates,T); v(:,1)=Inf; v(1,1)=0; +prev = zeros(nStates,T); in_u = zeros(nStates,T); + +for k=1:T + if k==1, vprev=inf(nStates,1); vprev(1)=0; else, vprev=v(:,k-1); end + vcur = inf(nStates,1); pre=zeros(nStates,1); inb=zeros(nStates,1); + + feat = getFeat(k); % either scalar y(k) or standardized window + for t=1:numel(trans) + sp=trans(t).sp; s=trans(t).s; u=trans(t).u; + ck = BMfun(feat, sp, s, u); + cand = vprev(sp)+ck; + if cand < vcur(s) + vcur(s)=cand; pre(s)=sp; inb(s)=u; + end + end + v(:,k)=vcur; prev(:,k)=pre; in_u(:,k)=inb; +end + +[~,st]=min(v(:,T)); +xhat=zeros(T,1); +for k=T:-1:1 + xhat(k)=in_u(st,k); + st=prev(st,k); if st==0, st=1; end +end +bhat = xhat>0; +end diff --git a/projects/ML_based_MLSE/model.m b/projects/ML_based_MLSE/model.m index d1da091..dd8c018 100644 --- a/projects/ML_based_MLSE/model.m +++ b/projects/ML_based_MLSE/model.m @@ -22,9 +22,9 @@ alpha = 0; doub_mode = db_mode.no_db; cols = linspecer(6); -rop = [-6]; +rop = [-5]; bwl = [0.5:0.1:1.5]; -fsym = [208:16:256].*1e9; +fsym = [212:16:256].*1e9; % nonlin_mod = [0.5:0.01:0.75]; nonlin_mod = ones(size(fsym)).*0.5; @@ -41,7 +41,7 @@ for r = 1:length(fsym) apply_pulsef = 1; [Digi_sig,Symbols,Tx_bits] = PAMsource(... - "fsym",fsym(r),"M",M,"order",17,"useprbs",0,... + "fsym",fsym(r),"M",M,"order",18,"useprbs",0,... "fs_out",fdac,... "applyclipping",0,"clipfactor",1.5,... "applypulseform",apply_pulsef,"pulseformer",Pform,... @@ -94,59 +94,87 @@ for r = 1:length(fsym) [~, 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); - %% Implement DSP directly here: + %% mu_lms = 0.0005; - % tic - % eq = FFE_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",32,"L",1); - % [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); - % toc + 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); - tic - eq = ML_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",32,"L",1); - [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); - toc - - Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(Eq_signal); - [~, errors, ber, ~] = calc_ber(Eq_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); - fprintf('FFE: %.2e \n',ber); + % Postfilter + [y_white,whitened_noise] = pf_.process(y_ffe, ffe_noise); - Vit_signal_ = Vit_signal; - Vit_signal_.signal = circshift(Vit_signal.signal,0); - Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(Vit_signal_); - [~, errors, ber, errpos] = calc_ber(Vit_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); - fprintf('Viterbi: %.2e \n',ber); + % 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); + + Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse); + [~, errors, ber_mlse_normal, errpos] = calc_ber(Vit_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); + % showLevelHistogram(y_white,Symbols,"displayname",'ffe','fignum',111); + + %% optimize length + + mu_lms = 0.15; + eq = ML_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^14,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",5,"sps",2,... + "traceback_depth",128,"L",2,"delta",0); + + [y_ml_mlse,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); + y_ml_mlse_ = y_ml_mlse; + y_ml_mlse_.signal = circshift(y_ml_mlse.signal,0); + Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse_); + [~, errors, ber, errpos] = calc_ber(Vit_bits.signal, Tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('ML MLSE: %.2e \n',ber); + + bursts = count_error_bursts(errpos, 10); + e = zeros(size(Vit_bits.signal)); + e(errpos) = 1; + figure(8) + stem(e) + + %% optimize delta + deltas = [-1:4]; + ber = zeros(1,length(deltas)); + parfor m = 1:numel(deltas) + + mu_lms = 0.2; + eq = 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] = eq.process(y_ffe,Symbols); + y_ml_mlse_ = y_ml_mlse; + y_ml_mlse_.signal = circshift(y_ml_mlse.signal,0); + Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse_); + [~, errors, ber(m), errpos] = calc_ber(Vit_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'); - - %% optimize smth. - tr_len = 2.^[2:15]; - tr_len = floor(tr_len); - ber = zeros(size(tr_len)); - for m = 1:numel(tr_len) - - mu_lms = 0.0005; - eq = ML_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"traceback_depth",tr_len(m)); - [Eq_signal,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); - - % Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(Eq_signal); - % [~, errors, ber(m), ~] = calc_ber(Eq_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); - - Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(Vit_signal); - [~, errors, ber(m), errpos] = calc_ber(Vit_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); - end - - figure(5); hold on - title('1-SPS') - plot(tr_len,ber,'DisplayName','BER'); - xlabel('Traceback Length') - beautifyBERplot - legend - ylim([1e-4, 1e-1]); - set(gca,'YScale','log'); + %% RUN Comparison @@ -163,12 +191,12 @@ for r = 1:length(fsym) 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",1,"dd_mode",1,"adaption_technique","lms"); + 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_1sps, Symbols, Tx_bits, ... + [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", [],... From 39bc8243fc0b828529638dcad89e37e215c4d367 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Mon, 3 Nov 2025 08:17:39 +0100 Subject: [PATCH 26/30] Few more scripts to evaluate new ML-MLSE Equalizer - which is not better!!! :-( --- Classes/04_DSP/Equalizer/ML_MLSE.m | 182 +++++++++------- projects/IMDD_base_system/imdd_it.m | 2 +- projects/ML_based_MLSE/minimal_model_gpt.m | 213 ------------------- projects/ML_based_MLSE/model.m | 66 +++--- projects/ML_based_MLSE/rate_evaluation.m | 109 ++++++++++ projects/ML_based_MLSE/rop_evaluation.m | 95 +++++++++ projects/ML_based_MLSE/standard_link_model.m | 95 +++++++++ 7 files changed, 443 insertions(+), 319 deletions(-) delete mode 100644 projects/ML_based_MLSE/minimal_model_gpt.m create mode 100644 projects/ML_based_MLSE/rate_evaluation.m create mode 100644 projects/ML_based_MLSE/rop_evaluation.m create mode 100644 projects/ML_based_MLSE/standard_link_model.m diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index e09d2cd..bb327b1 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -53,6 +53,8 @@ classdef ML_MLSE < handle state_dict % containers.Map: key(sequence)->state index key_fmt = '%.8g_'; % key format for sequence strings nSym % |constellation| + + ber = [] end methods @@ -182,7 +184,7 @@ classdef ML_MLSE < handle % ============================================================== % FFE + Whitening + ML-Based Branch Metric Estimation + Viterbi % ============================================================== - debug = 0; + debug = 1; % --- Input padding and preallocation y = zeros(N,1); @@ -199,19 +201,34 @@ classdef ML_MLSE < handle pred = zeros(nSymbols, obj.nStates, 'uint32'); pm_sto = nan(obj.nStates, nSymbols,'like',pm); - % --- Initialize "true" trellis state for training (shift-register style) - % expect sequences in chronological order [x_{k-L+1}:x_k], but combs rows are [x_k, x_{k-1}, ...] - if numel(d) >= obj.L - init_seq = d(1:obj.L); % [x_1 ... x_L] - true_to_state_idx = obj.state_dict(obj.seq_key(flip(init_seq))); % flip to [x_L, x_{L-1}, ...] + + %%% START IDX + if 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 + start_sample = 1; + end_sample = start_sample + (ceil(N/obj.sps)-1)*obj.sps; else - true_to_state_idx = 1; + start_sample = 1; + end_sample = N; + end + + start_symbol = 1 + floor((start_sample - 1)/obj.sps); % ABSOLUTE symbol index + + if numel(d) >= obj.L && start_symbol >= obj.L + init_seq = d(start_symbol-obj.L+1 : start_symbol); % [d_k-L+1 ... d_k] + true_to_state_idx = obj.state_dict(obj.seq_key(flip(init_seq))); % [d_k ... d_k-L+1] + else + % Not enough history – fall back to state 1 + true_to_state_idx = uint32(1); end symbol = 0; - for sample = 1:obj.sps:N + for sample = start_sample:obj.sps:end_sample symbol = symbol + 1; k = symbol; + sym_idx = start_symbol + (symbol - 1); % --- Build Δ-delayed observation window y_k i1 = sample - obj.Nf + 1 + obj.delta; @@ -232,78 +249,72 @@ classdef ML_MLSE < handle v_tilde = pm(obj.valid_from_idx) + c_hat; % [nFeasible×1] % ===== Gradient update (Algorithm 1) ===== - % if training - - if k > obj.L - % shift-register: previous "to" becomes current "from" - true_from_state_idx = true_to_state_idx; - - % current "to" from data window - curr_seq = d(k-obj.L+1:k); - key_to = obj.seq_key(flip(curr_seq)); - if isKey(obj.state_dict, key_to) - true_to_state_idx = obj.state_dict(key_to); - else - % fall back safely (should not happen with proper constellation) - true_to_state_idx = true_from_state_idx; - end - else - % not enough history yet - true_from_state_idx = 1; - true_to_state_idx = true_to_state_idx; % keep init - end - - if 0 - disp(['FROM: state',char(num2str(true_from_state_idx)),' : symbol transition', char(num2str(obj.combs(true_from_state_idx,:)))]); - disp(['TO: state',char(num2str(true_to_state_idx)),' : symbol transition', char(num2str(obj.combs(true_to_state_idx,:)))]); - end - - % Dirac delta over correct extended transition (from,to) - dirac = zeros(obj.nFeasible,1); - dirac(obj.valid_from_idx==true_from_state_idx & obj.valid_to_idx==true_to_state_idx) = 1; % This Dirac delta function δ(s = s∗ k, s′ = s∗ k−1) = 1 if the extended state (s, s′) corresponds to the true realized states (s∗ k, s∗ k−1), and is zero otherwise. - - % softmax over -v_tilde (numerically safe shift) - p = exp(-(v_tilde - max(v_tilde))); - p = p./sum(p); % found in formula (9) and (19) - - % gradient term (t - p) - dmp = (dirac - p)'; % 1×nFeasible - - if mod(symbol,128) == 1 && debug - % --- Normalize and compute probabilities - v_norm = v_tilde - max(v_tilde); - probs_lin = exp(-v_norm); probs_lin = probs_lin ./ sum(probs_lin); - probs_log = -v_norm; - - % --- Map back into nStates×nStates grid - probs_mat = nan(obj.nStates, obj.nStates); - probs_logmat = nan(obj.nStates, obj.nStates); - probs_mat(obj.valid) = probs_lin; - probs_logmat(obj.valid) = probs_log; - - % --- Identify the current true transition - [to_idx, from_idx] = find(obj.valid); - cur_idx = find(dirac==1); - cur_to = to_idx(cur_idx); - cur_from = from_idx(cur_idx); - - figure(11); clf; - imagesc(probs_logmat); axis xy; colorbar; - xlabel('From state'); ylabel('To state'); set(gca,'FontSize',10); - hold on; - plot(cur_from, cur_to, 'rs', 'MarkerSize', 10, 'LineWidth', 2, 'MarkerFaceColor', 'none'); - hold off; - end if 1 %training - % dmp is large for the correct transition -> update emphasizes that branch - dL_Dw = dmp .* (yk); % ∂CE/∂(w) - formula (10) - % only start with updates when we are inside the signal - if k > obj.L - obj.w = obj.w - mu * dL_Dw; % (Nf+1)×nFeasible + % previous "to" becomes current "from" (shift-register) + true_from_state_idx = true_to_state_idx; + + % --- Build current "to" state from ABSOLUTE symbol index + if sym_idx >= obj.L + curr_seq = d(sym_idx-obj.L+1 : sym_idx); % [d_k-L+1 ... d_k] + key_to = obj.seq_key(flip(curr_seq)); % -> [d_k ... d_k-L+1] + if isKey(obj.state_dict, key_to) + true_to_state_idx = obj.state_dict(key_to); + else + % Fall back safely (should not happen with proper constellation) + true_to_state_idx = true_from_state_idx; + end + else + % Not enough history yet for a full L-symbol state + % keep previous 'to' and 'from' + true_to_state_idx = true_to_state_idx; + true_from_state_idx = true_from_state_idx; end + + % Dirac delta over correct extended transition (from,to) + dirac = zeros(obj.nFeasible,1); + dirac(obj.valid_from_idx==true_from_state_idx & ... + obj.valid_to_idx ==true_to_state_idx) = 1; + + % softmax over -v_tilde (numerically safe shift) + p = exp(-(v_tilde - max(v_tilde))); + p = p./(sum(p)+eps); + + % gradient term (t - p) + dmp = (dirac - p)'; % 1×nFeasible + + % Per-feature gradient; implicit expansion gives (Nf+1)×nFeasible + dL_Dw = (yk) .* dmp; + + % Start updates only when the ABSOLUTE symbol index has ≥ L history + if sym_idx >= obj.L + + + obj.w = obj.w - ones(size(dL_Dw,1),1).*mu .* dL_Dw; % (Nf+1)×nFeasible + % obj.w = obj.w - mu * dL_Dw; % (Nf+1)×nFeasible + 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 (matrix form, min of costs) v_tilde_mat = inf(obj.nStates, obj.nStates); v_tilde_mat(obj.valid) = v_tilde; @@ -327,10 +338,21 @@ classdef ML_MLSE < handle y_vit = obj.first_sym(viterbi_path); y = obj.first_sym(viterbi_path); - if 1 %debug || training - err = sum(y ~= d(1:length(y))); - ser = err./length(y); - fprintf('Epoch: %d - SER: %.1e \n',epoch, ser); + 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))); + + 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; + + % ser = err./length(y); + % fprintf('Epoch: %d - SER: %.1e \n',epoch, ser); figure(10); subplot(2,2,1:2); @@ -347,6 +369,8 @@ classdef ML_MLSE < handle scatter(1:symbol,pm_sto,1,'.') % plot(1:symbol,pm_sto,'LineStyle','none') title('Path Metric Winners') + + drawnow end end end 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/ML_based_MLSE/minimal_model_gpt.m b/projects/ML_based_MLSE/minimal_model_gpt.m deleted file mode 100644 index 9536774..0000000 --- a/projects/ML_based_MLSE/minimal_model_gpt.m +++ /dev/null @@ -1,213 +0,0 @@ -function minimal_model_gpt -% Minimal binary IM/DD + ISI (M=3), AWGN. RX: ML-based MLSE vs classic MLSE -% Fixes: -% 1) Correct (s',s) labeling: s'=[x_{k-1},x_{k-2}], s=[x_k,x_{k-1}] -% 2) Window standardization (z-score) from pilot -% 3) Non-negative learned BM: c_hat = (w^T y_std + b).^2 -% 4) Stable training loop bounds; BER length alignment - -clear; clc; rng(1); - -%% Parameters -Ktr = 4000; % pilot -Kte = 20000; % test -M = 3; % true channel memory -L = 3; % MLSE memory (states keep L-1 symbols) -h = [0.55 1.00 0.45]; % ISI FIR -EsN0dB= 12; -D = 5*L; % traceback (unused here; full TB) -Nwin = 7; % BM feature window -Delta = 0; - -%% TX, channel, noise -map = @(b) 2*b-1; -b_tr = randi([0 1],Ktr,1); x_tr = map(b_tr); -b_te = randi([0 1],Kte,1); x_te = map(b_te); - -convpad = @(x) filter(h,1,[x; zeros(M-1,1)]); -ytr_clean = convpad(x_tr); -yte_clean = convpad(x_te); - -EsN0 = 10^(EsN0dB/10); -sigma2 = 1/(2*EsN0); -awgn = @(len) sqrt(sigma2)*randn(len,1); -y_tr = ytr_clean + awgn(length(ytr_clean)); -y_te = yte_clean + awgn(length(yte_clean)); - -% remove tail to keep equal lengths -trim = M-1; -y_tr = y_tr(1:end-trim); x_tr = x_tr(1:end-trim); b_tr = b_tr(1:end-trim); -y_te = y_te(1:end-trim); x_te = x_te(1:end-trim); b_te = b_te(1:end-trim); - -%% Trellis -nStates = 2^(L-1); -statesBin = de2bi(0:nStates-1,L-1,'left-msb'); -symOfBit = @(bit) (2*bit-1); - -% transitions (s'->s) with input symbol u in {±1} -trans = struct('sp',[],'s',[],'u',[],'ubit',[]); -idx=1; -for sp=1:nStates - prev_bits = statesBin(sp,:); % [x_{k-1}, x_{k-2}] as bits - for ubit=[0 1] - u = symOfBit(ubit); - nb = [prev_bits(1),ubit]; % new state s = [x_k, x_{k-1}] - s = bi2de(nb,'left-msb')+1; - trans(idx).sp=sp; trans(idx).s=s; trans(idx).u=u; trans(idx).ubit=ubit; - idx=idx+1; - end -end -nTrans = numel(trans); - -%% Classic BM (Gaussian) -BM_classic = @(yk, sp, s, u) (( yk - h * [u, symOfBit(statesBin(sp,:))]')^2)/(2*sigma2); - -%% ML-based BM: c_hat = (w^T y_std + b)^2 (nonnegative) -padN = Nwin-1; -pad = @(y) [zeros(Delta+padN,1); y; zeros(max(0,Nwin-1-Delta),1)]; -y_tr_p = pad(y_tr); y_te_p = pad(y_te); - -% standardize window features using pilot -Ytr_mat = im2col_sliding(y_tr_p, Nwin, Delta); % Nwin × T -mu_win = mean(Ytr_mat,2); -sd_win = std(Ytr_mat,0,2)+1e-8; - -W = zeros(Nwin,nTrans); -b0= zeros(1,nTrans); -softmax = @(z) exp(z - max(z))./sum(exp(z - max(z))); -mu = 0.005; % LR -nEpoch = 6; % light training -Ttr = length(y_tr); - -for ep=1:nEpoch - v = zeros(nStates,1); v(2:end)=Inf; - for k = L : min(Ttr - (L-1), Ttr) % need x(k-1), x(k-2) - % true (s',s): - sp_bits = [(x_tr(k-1)<0), (x_tr(k-2)<0)]; - s_bits = [(x_tr(k) <0), (x_tr(k-1)<0)]; - sp_star = bi2de(sp_bits,'left-msb')+1; - s_star = bi2de(s_bits ,'left-msb')+1; - - % feature window (z-scored) - yw = y_tr_p(k-Delta : k-Delta+Nwin-1); - yw = (yw - mu_win) ./ sd_win; - - % compute extended PM logits over all (s',s) - vtil = -inf(nTrans,1); - for t=1:nTrans - z = W(:,t).'*yw + b0(t); - c_hat = z*z; % (.)^2 nonnegative BM - vtil(t) = -( v(trans(t).sp) + c_hat); - end - pext = softmax(vtil).'; - t_star = find([trans.sp]==sp_star & [trans.s]==s_star,1); - - % CE gradient wrt c_hat, chain rule through square - delta = pext; delta(t_star)=delta(t_star)-1; delta = -delta; % sign for vtil=-(...) - for t=1:nTrans - z = W(:,t).'*yw + b0(t); - dc_dz = 2*z; % d(z^2)/dz - g = delta(t) * dc_dz; % ∂L/∂z - W(:,t) = W(:,t) - mu * g * yw; - b0(t) = b0(t) - mu * g; - end - - % PM update for next step - v_next = inf(nStates,1); - for t=1:nTrans - sp=trans(t).sp; s=trans(t).s; - z = W(:,t).'*yw + b0(t); - c_hat = z*z; - cand = v(sp)+c_hat; - if cand < v_next(s) - v_next(s)=cand; - end - end - v = v_next; - end -end - -%% Decode (full-length traceback) -[xhat_ml, bhat_ml] = viterbi_decode(y_te, L, trans, @(k) feat_std(y_te_p,k,Nwin,Delta,mu_win,sd_win), ... - @(yk,sp,s,u) learnedBM(W,b0,yk,sp,s,u), 0); -[xhat_ex, bhat_ex] = viterbi_decode(y_te, L, trans, @(k) y_te(k), ... - @(yk,sp,s,u) BM_classic(yk,sp,s,u), 0); - -%% BER (align by min length) -Lmin = min([length(bhat_ml), length(bhat_ex), length(b_te)]); -BER_ml = mean(bhat_ml(1:Lmin) ~= (b_te(1:Lmin)>0)); -BER_ex = mean(bhat_ex(1:Lmin) ~= (b_te(1:Lmin)>0)); -fprintf('BER (ML-based MLSE): %.3e\n', BER_ml); -fprintf('BER (classic MLSE ): %.3e\n', BER_ex); -end - -% ----------------- helpers ----------------- -function Y = im2col_sliding(y_pad, Nwin, Delta) -T = length(y_pad) - (Nwin-1) - Delta; -Y = zeros(Nwin,T); -for k=1:T - Y(:,k) = y_pad(k-Delta : k-Delta+Nwin-1); -end -end - -function yw = feat_std(y_pad,k,Nwin,Delta,mu_win,sd_win) -yw = y_pad(k-Delta : k-Delta+Nwin-1); -yw = (yw - mu_win) ./ sd_win; -end - -function c = learnedBM(W,b0,yw,sp,s,~) -% pick parameters of the (sp->s) transition -persistent map; -if isempty(map) - % build once: index of W/b0 for each (sp,s) - nStates = size(W,1)*0+1; %#ok -end -% linear scan is fine at this size: -% (use first match of (sp,s)) -c = inf; -for t=1:size(W,2) - % suppose we stored (sp,s) order as in training; we cannot access here. - % Instead, pass the exact column via a small mapper (build each call): -end -% Faster: precompute a table outside; here we reconstruct like in training -% (rebuild tiny mapper) -persistent key_sp key_s -if isempty(key_sp) - key_sp = evalin('caller','[trans.sp]'); - key_s = evalin('caller','[trans.s]'); -end -t = find(key_sp==sp & key_s==s,1); -z = W(:,t).'*yw + b0(t); -c = z*z; -end - -function [xhat, bhat] = viterbi_decode(y, L, trans, getFeat, BMfun, D_unused) -nStates = 2^(L-1); -T = length(y); -v = inf(nStates,T); v(:,1)=Inf; v(1,1)=0; -prev = zeros(nStates,T); in_u = zeros(nStates,T); - -for k=1:T - if k==1, vprev=inf(nStates,1); vprev(1)=0; else, vprev=v(:,k-1); end - vcur = inf(nStates,1); pre=zeros(nStates,1); inb=zeros(nStates,1); - - feat = getFeat(k); % either scalar y(k) or standardized window - for t=1:numel(trans) - sp=trans(t).sp; s=trans(t).s; u=trans(t).u; - ck = BMfun(feat, sp, s, u); - cand = vprev(sp)+ck; - if cand < vcur(s) - vcur(s)=cand; pre(s)=sp; inb(s)=u; - end - end - v(:,k)=vcur; prev(:,k)=pre; in_u(:,k)=inb; -end - -[~,st]=min(v(:,T)); -xhat=zeros(T,1); -for k=T:-1:1 - xhat(k)=in_u(st,k); - st=prev(st,k); if st==0, st=1; end -end -bhat = xhat>0; -end diff --git a/projects/ML_based_MLSE/model.m b/projects/ML_based_MLSE/model.m index dd8c018..4610b72 100644 --- a/projects/ML_based_MLSE/model.m +++ b/projects/ML_based_MLSE/model.m @@ -18,13 +18,12 @@ laser_linewidth = 1e6; % Channel link_length = 0; -alpha = 0; doub_mode = db_mode.no_db; cols = linspecer(6); -rop = [-5]; +rop = [-8]; bwl = [0.5:0.1:1.5]; -fsym = [212:16:256].*1e9; +fsym = [160:16:256].*1e9; % nonlin_mod = [0.5:0.01:0.75]; nonlin_mod = ones(size(fsym)).*0.5; @@ -100,6 +99,7 @@ for r = 1:length(fsym) %% 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); @@ -116,49 +116,63 @@ for r = 1:length(fsym) % 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); - - Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse); - [~, errors, ber_mlse_normal, errpos] = calc_ber(Vit_bits.signal, Tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + 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); - % showLevelHistogram(y_white,Symbols,"displayname",'ffe','fignum',111); - - %% optimize length - - mu_lms = 0.15; - eq = ML_MLSE("epochs_tr",5,"epochs_dd",5,"len_tr",2^14,... - "mu_dd",mu_lms,"mu_tr",mu_lms,"order",5,"sps",2,... - "traceback_depth",128,"L",2,"delta",0); - - [y_ml_mlse,Vit_signal] = eq.process(Rx_sig_2sps,Symbols); - y_ml_mlse_ = y_ml_mlse; - y_ml_mlse_.signal = circshift(y_ml_mlse.signal,0); - Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse_); - [~, errors, ber, errpos] = calc_ber(Vit_bits.signal, Tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); - fprintf('ML MLSE: %.2e \n',ber); bursts = count_error_bursts(errpos, 10); - e = zeros(size(Vit_bits.signal)); + 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; - eq = ML_MLSE("epochs_tr",2,"epochs_dd",5,"len_tr",2^13,... + 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] = eq.process(y_ffe,Symbols); + [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); - Vit_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse_); - [~, errors, ber(m), errpos] = calc_ber(Vit_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); + 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 diff --git a/projects/ML_based_MLSE/rate_evaluation.m b/projects/ML_based_MLSE/rate_evaluation.m new file mode 100644 index 0000000..07e4057 --- /dev/null +++ b/projects/ML_based_MLSE/rate_evaluation.m @@ -0,0 +1,109 @@ + +ber_ffe = []; +ber_mlse = []; +ber_dbtgt = []; +ber_ml = []; + +mlse = 1; +dbtgt = 1; + +baudrates = [136:8:224].*1e9; +parfor 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",1300,"link_length_m",0,"random_key",1,"apply_pulsef",0); + % [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; + 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",30,"epochs_dd",1,"len_tr",2^15,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",5,"sps",1,... + "traceback_depth",128,"L",3,"delta",0); + + [y_ml_mlse,~] = ml_mlse_equalizer.process(y_white,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/rop_evaluation.m b/projects/ML_based_MLSE/rop_evaluation.m new file mode 100644 index 0000000..68ed3eb --- /dev/null +++ b/projects/ML_based_MLSE/rop_evaluation.m @@ -0,0 +1,95 @@ + +ber_ffe = []; +ber_mlse = []; +ber_dbtgt = []; +ber_ml = []; + +mlse = 1; +dbtgt = 1; + +rops = linspace(-15,-5,12); +parfor i = 1:length(rops) + + rop = rops(i); + M = 4; + [Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1] = standard_link_model("M",M,"fsym",224e9,"rop",rop,"laser_linewidth",1300,"link_length_m",0,"random_key",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; + 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 + + %% RUN ML-Based MLSE + + mu_lms = 0.15; + ml_mlse_equalizer = ML_MLSE("epochs_tr",30,"epochs_dd",1,"len_tr",2^14,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",4,"sps",2,... + "traceback_depth",128,"L",2,"delta",0); + + [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(3); hold on; +if mlse +plot(rops,ber_ffe,'DisplayName','FFE'); +plot(rops,ber_mlse,'DisplayName','MLSE'); +end +if dbtgt +plot(rops,ber_dbtgt,'DisplayName','DB tgt'); +end +plot(rops,ber_ml,'DisplayName','ML-MLSE'); +beautifyBERplot; +legend + + + + + + + + + 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..2412af1 --- /dev/null +++ b/projects/ML_based_MLSE/standard_link_model.m @@ -0,0 +1,95 @@ +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_m (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_m, ... + "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); + + Rx_sig_2sps = Scpe_cell{1}.normalize("mode","rms"); + +end From 0080cb2264aed028a7c7dda745134100b190bf7b Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 12 Nov 2025 09:24:02 +0100 Subject: [PATCH 27/30] ML Equalizer works now. Not yet perfectly integrated into all the routines --- Classes/04_DSP/Equalizer/ML_MLSE.m | 235 +++-- Classes/04_DSP/Sequence Detection/MLSE.m | 2 +- Classes/DataBaseHandler/Metricstruct.m | 4 +- .../fwm_plots/CompleteRoutine.m | 502 +++++------ .../CompleteRoutine_DifferentChannels_Fig3.m | 500 +++++------ .../fwm_plots/automate_JLT_plots.m | 830 +++++++++--------- .../fwm_plots/automate_PTL_plot_new.m | 0 .../fwm_plots/dispersion_only.m | 166 ++-- .../dispersion_validation_miniskript.m | 102 +-- .../fwm_plots/generatePlots.m | 120 +-- .../fwm_plots/plot3dCurve.m | 496 +++++------ .../fwm_plots/plotBerVsZDW.m | 598 ++++++------- .../fwm_plots/plotBerVsZdwFailureRate.m | 312 +++---- .../fwm_plots/plotChannelSpacingAna.m | 102 +-- .../fwm_plots/plotCurve.m | 588 ++++++------- .../fwm_plots/plotHistogram.m | 300 +++---- .../fwm_plots/plotViolin.m | 410 ++++----- .../fwm_plots/plot_ber_distribution.m | 114 +-- Datatypes/equalizer_structure.m | 1 + Functions/EQ_structures/dsp_runid.m | 48 +- Functions/EQ_structures/duobinary_signaling.m | 5 +- Functions/EQ_structures/ml_mlse.m | 113 +++ .../configureEqualizers_remove.m | 40 - Functions/Job_Processing/preprocessSignal.m | 6 +- Functions/Job_Processing/submitJobs.m | 1 + Functions/beautifyBERplot.m | 124 ++- .../Auswertung_JLT/ber_vs_dispersion.m | 2 +- .../Auswertung_JLT/plot_measurements_gpt.m | 23 +- .../Auswertung_JLT/run_dsp_from_db.m | 150 +++- .../Auswertung_JLT/run_plot_measurements.m | 22 +- .../ML_based_MLSE/analyze_filter_length.m | 164 ++++ projects/ML_based_MLSE/analyze_mu.m | 113 +++ projects/ML_based_MLSE/experimental_data.m | 162 ++++ projects/ML_based_MLSE/interp_fec_cross.m | 13 + .../ML_based_MLSE/minimal_example_huawei.zip | Bin 0 -> 14741 bytes .../minimal_example_huawei/bcjr_pam.m | 555 ++++++++++++ .../minimal_example_huawei/minimal_example.m | 193 ++++ .../minimal_example_huawei/ml_mlse_pam.m | 473 ++++++++++ projects/ML_based_MLSE/rate_evaluation.m | 23 +- projects/ML_based_MLSE/read_csv.m | 30 + projects/ML_based_MLSE/rop_evaluation.m | 208 +++-- .../ML_based_MLSE/rrop_vs_length_evaluation.m | 140 +++ projects/ML_based_MLSE/standard_link_model.m | 10 +- .../theoretic_channel_evaluation.m | 157 ++++ 44 files changed, 5289 insertions(+), 2868 deletions(-) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/CompleteRoutine.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/CompleteRoutine_DifferentChannels_Fig3.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/automate_JLT_plots.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/automate_PTL_plot_new.m (100%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/dispersion_only.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/dispersion_validation_miniskript.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/generatePlots.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/plot3dCurve.m (97%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/plotBerVsZDW.m (97%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/plotBerVsZdwFailureRate.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/plotChannelSpacingAna.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/plotCurve.m (97%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/plotHistogram.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/plotViolin.m (96%) rename Classes/Warehouse_class/functions/{ => phase_predist_plots}/fwm_plots/plot_ber_distribution.m (96%) create mode 100644 Functions/EQ_structures/ml_mlse.m delete mode 100644 Functions/Job_Processing/configureEqualizers_remove.m create mode 100644 projects/ML_based_MLSE/analyze_filter_length.m create mode 100644 projects/ML_based_MLSE/analyze_mu.m create mode 100644 projects/ML_based_MLSE/experimental_data.m create mode 100644 projects/ML_based_MLSE/interp_fec_cross.m create mode 100644 projects/ML_based_MLSE/minimal_example_huawei.zip create mode 100644 projects/ML_based_MLSE/minimal_example_huawei/bcjr_pam.m create mode 100644 projects/ML_based_MLSE/minimal_example_huawei/minimal_example.m create mode 100644 projects/ML_based_MLSE/minimal_example_huawei/ml_mlse_pam.m create mode 100644 projects/ML_based_MLSE/read_csv.m create mode 100644 projects/ML_based_MLSE/rrop_vs_length_evaluation.m create mode 100644 projects/ML_based_MLSE/theoretic_channel_evaluation.m diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index bb327b1..4539f62 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -1,13 +1,41 @@ classdef ML_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); + % 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 @@ -24,6 +52,8 @@ classdef ML_MLSE < handle mu_dd %weight update in dd mode epochs_dd + adaptive_mu + constellation L %viterbi memory length @@ -50,11 +80,13 @@ classdef ML_MLSE < handle w % --- New: fast state lookup --- + 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 @@ -72,6 +104,8 @@ classdef ML_MLSE < handle options.mu_dd = 1e-5; options.epochs_dd = 5; + options.adaptive_mu = 1; + options.delta = 0; options.traceback_depth = 1024; @@ -180,11 +214,12 @@ classdef ML_MLSE < handle X_viterbi = X_viterbi.logbookentry(lbdesc); % append to logbook end - function [y,y_vit] = equalize(obj,x,d,mu,epochs,N,training) + function [y,y_ref] = equalize(obj,x,d,mu,epochs,N,training) % ============================================================== % FFE + Whitening + ML-Based Branch Metric Estimation + Viterbi % ============================================================== debug = 1; + showPlots = 1; % --- Input padding and preallocation y = zeros(N,1); @@ -200,7 +235,8 @@ classdef ML_MLSE < handle v_tilde = zeros(1,obj.nFeasible); pred = zeros(nSymbols, obj.nStates, 'uint32'); pm_sto = nan(obj.nStates, nSymbols,'like',pm); - + CE_accum = 0; + %%% START IDX if training @@ -210,7 +246,7 @@ classdef ML_MLSE < handle start_sample = 1; end_sample = start_sample + (ceil(N/obj.sps)-1)*obj.sps; else - start_sample = 1; + start_sample = 1;%obj.len_tr; end_sample = N; end @@ -251,34 +287,72 @@ classdef ML_MLSE < handle % ===== Gradient update (Algorithm 1) ===== if 1 %training - % previous "to" becomes current "from" (shift-register) - true_from_state_idx = true_to_state_idx; - - % --- Build current "to" state from ABSOLUTE symbol index - if sym_idx >= obj.L - curr_seq = d(sym_idx-obj.L+1 : sym_idx); % [d_k-L+1 ... d_k] - key_to = obj.seq_key(flip(curr_seq)); % -> [d_k ... d_k-L+1] - if isKey(obj.state_dict, key_to) - true_to_state_idx = obj.state_dict(key_to); - else - % Fall back safely (should not happen with proper constellation) - true_to_state_idx = true_from_state_idx; - end - else - % Not enough history yet for a full L-symbol state - % keep previous 'to' and 'from' - true_to_state_idx = true_to_state_idx; - true_from_state_idx = true_from_state_idx; + % --- allocate storage once + if epoch == 1 && symbol == 1 + obj.true_to_state_idx = ones(ceil(N/obj.sps),1,'uint32'); end - % Dirac delta over correct extended transition (from,to) + % --- 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 + + % --- reuse cached state from second epoch onward + true_to_state_idx = obj.true_to_state_idx(symbol); + + % --- ensure valid (from,to) dirac = zeros(obj.nFeasible,1); - dirac(obj.valid_from_idx==true_from_state_idx & ... - obj.valid_to_idx ==true_to_state_idx) = 1; + mask = obj.valid_from_idx==true_from_state_idx & ... + obj.valid_to_idx ==true_to_state_idx; + 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) - p = exp(-(v_tilde - max(v_tilde))); - p = p./(sum(p)+eps); + v_shift = -(v_tilde - min(v_tilde)); % shift to small positive numbers + v_shift = min(v_shift, 100); % clamp exponent argument (≈ exp(50)=3e21) + expv = exp(v_shift); + p = expv ./ (sum(expv) + eps); + + % for logging only: + 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); %use 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; + % gradient term (t - p) dmp = (dirac - p)'; % 1×nFeasible @@ -288,10 +362,14 @@ classdef ML_MLSE < handle % Start updates only when the ABSOLUTE 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 - - obj.w = obj.w - ones(size(dL_Dw,1),1).*mu .* dL_Dw; % (Nf+1)×nFeasible - % obj.w = obj.w - mu * dL_Dw; % (Nf+1)×nFeasible + obj.w = obj.w - mu_eff .* dL_Dw; % (Nf+1)×nFeasible end % if debug && epoch > 2 @@ -335,42 +413,69 @@ classdef ML_MLSE < handle viterbi_path(n-1) = pred(n, viterbi_path(n)); end - y_vit = obj.first_sym(viterbi_path); + y_ref = d(start_symbol:end); y = obj.first_sym(viterbi_path); - if debug %&& training + 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))); - 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; + try + 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; + catch + ser = err./length(y); + fprintf('Epoch: %d - SER: %.1e \n',epoch, ser); + end - % ser = err./length(y); - % fprintf('Epoch: %d - SER: %.1e \n',epoch, ser); + obj.ce(epoch) = CE_accum./symbol; - figure(10); - subplot(2,2,1:2); - heatmap(obj.w); - title('Filter') - - subplot(2,2,3); - v_tildemat = NaN(obj.nStates, obj.nStates); - v_tildemat(obj.valid) = v_tilde; % log-domain scores - heatmap(v_tildemat); - title('Path Metrics (v_tilde)') - - subplot(2,2,4); - scatter(1:symbol,pm_sto,1,'.') - % plot(1:symbol,pm_sto,'LineStyle','none') - title('Path Metric Winners') - - drawnow + 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('Path Metrics (v_tilde)') + + subplot(3,2,4); + scatter(1:symbol,pm_sto,1,'.') + title('Path Metric Winners') + + 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 + + % Left y-axis: Cross Entropy (linear) + yyaxis left + scatter(1:length(obj.ce), obj.ce, 10, 's', 'filled') + ylabel('Cross Entropy') + + % Right y-axis: BER (logarithmic) + yyaxis right + scatter(1:length(obj.ber), obj.ber, 10, 'd', 'filled') + set(gca, 'YScale', 'log') + ylabel('BER (log scale)') + + xlim([1, epochs]) + xlabel('Epoch') + title('Cross Entropy // BER') + grid on + + drawnow + end end end end diff --git a/Classes/04_DSP/Sequence Detection/MLSE.m b/Classes/04_DSP/Sequence Detection/MLSE.m index fabee6f..59ef211 100644 --- a/Classes/04_DSP/Sequence Detection/MLSE.m +++ b/Classes/04_DSP/Sequence Detection/MLSE.m @@ -118,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 diff --git a/Classes/DataBaseHandler/Metricstruct.m b/Classes/DataBaseHandler/Metricstruct.m index 403e90c..f95311c 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} = [] 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 96% 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 cc2c8d6..801c0e8 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,415 +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; - -% 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 = 2; -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 = 0; - - -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,6]; - 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 = [3]; -Len = [10]; - -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 - - - + + +%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 = 2; +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 = 0; + + +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,6]; + 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 = [3]; +Len = [10]; + +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/fwm_plots/automate_PTL_plot_new.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_PTL_plot_new.m similarity index 100% rename from Classes/Warehouse_class/functions/fwm_plots/automate_PTL_plot_new.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_PTL_plot_new.m 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 97% rename from Classes/Warehouse_class/functions/fwm_plots/plotCurve.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotCurve.m index e06e7e5..be3813e 100644 --- a/Classes/Warehouse_class/functions/fwm_plots/plotCurve.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotCurve.m @@ -1,294 +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; - -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 +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 96% rename from Classes/Warehouse_class/functions/fwm_plots/plotViolin.m rename to Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/plotViolin.m index 93cfc10..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/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 1a20686..976bcfa 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 @@ -98,9 +97,10 @@ try use_ffe = 0; use_dfe = 0; - use_vnle_mlse = 1; + 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, ... @@ -233,30 +233,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 = 250; + 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",2,"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 @@ -292,6 +287,7 @@ try if duob_mode == db_mode.db_encoded + mlse_db_enc = MLSE_viterbi("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..c484918 100644 --- a/Functions/EQ_structures/duobinary_signaling.m +++ b/Functions/EQ_structures/duobinary_signaling.m @@ -36,8 +36,9 @@ if ~isempty(options.postFFE) end % Process through MLSE -% [mlse_signal] = mlse_.process(eq_signal); -[mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); +[mlse_signal] = mlse_.process(eq_signal); +% 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/ml_mlse.m b/Functions/EQ_structures/ml_mlse.m new file mode 100644 index 0000000..abd3aa0 --- /dev/null +++ b/Functions/EQ_structures/ml_mlse.m @@ -0,0 +1,113 @@ +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(); +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 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..4828bba 100644 --- a/Functions/Job_Processing/preprocessSignal.m +++ b/Functions/Job_Processing/preprocessSignal.m @@ -16,9 +16,9 @@ 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); +Scpe_sig = Filter('filtdegree', 4, "f_cutoff", Symbols.fs.*0.6, ... + "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ... + "active", true).process(Scpe_sig); % Remove DC offset Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); 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/beautifyBERplot.m b/Functions/beautifyBERplot.m index 1a4f953..2d70cee 100644 --- a/Functions/beautifyBERplot.m +++ b/Functions/beautifyBERplot.m @@ -1,43 +1,97 @@ 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 + options.logscale (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 - % BEAUTIFYBERPLOT Enhances a BER plot for publication-quality figures. - % 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); - +% --- 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.1; + lines(i).LineStyle = '-'; + 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 + +% --- optional smoothing/fitting overlay +if options.polyfit + hold on 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 + x = lines(i).XData; + y = lines(i).YData; + valid = isfinite(x) & isfinite(y); + if sum(valid) < options.polyorder + 1 + continue; end - lines(i).MarkerSize = 7; % Marker size - lines(i).MarkerFaceColor = lines(i).Color; % Use line color for marker face - lines(i).MarkerEdgeColor = 'white'; + + 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 - - % 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. - if options.logscale - set(gca, 'YScale', 'log'); - end - - % 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'); + hold off +end + +% --- axis scaling and cosmetics +if options.logscale + set(gca, 'YScale', 'log'); +end + +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', 'Times New Roman'); + 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/plot_measurements_gpt.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m index 0b5cefd..d0b95a1 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m @@ -41,7 +41,9 @@ plotdefs = struct( ... 'legendLocation' , 'best', ... 'fecLineWidth' , 2.2, ... % thicker FEC limits 'fecColor' , [0.25 0.25 0.25], ... - 'capSize' , 6 ... + 'capSize' , 6, ... + 'lineStyle_default' , '-', ... + 'use_pre_emph_styling' , true ... ); cfg.plot = filldefaults(cfg.plot, plotdefs); @@ -131,8 +133,23 @@ for gi = 1:nG yu = yu(ord); ylo = ylo(ord); yhi = yhi(ord); % Styles - pre = logical(grpTbl.pre_emph(gi)); - ls = tern(pre, cfg.plot.lineStyle_pre_emph_on, cfg.plot.lineStyle_pre_emph_off); + % Decide if we style by pre_emph + canStyleByPre = cfg.plot.use_pre_emph_styling && ismember('pre_emph', T.Properties.VariableNames); + + if canStyleByPre + if any(strcmp(group_by,'pre_emph')) + % pre_emph is an explicit grouping key -> take it from the group table + pre = logical(grpTbl.pre_emph(gi)); + else + % pre_emph not grouped, but available in the rows -> infer from the members of this group + pre = logical(mode(T.pre_emph(G==gi))); + end + ls = tern(pre, cfg.plot.lineStyle_pre_emph_on, cfg.plot.lineStyle_pre_emph_off); + else + % no pre-emph styling → use a single default style + ls = cfg.plot.lineStyle_default; + end + lbl = buildLabel(grpTbl(gi,:), group_by); % Main line (dark) 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 ff9eac0..212a72f 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 = 2; experiment = "highspeed_2024"; dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' @@ -42,16 +42,16 @@ fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); M = 4; fp.where('Runs', 'pam_level','EQUALS', M); -fp.where('Runs', 'bitrate','EQUALS', 420e9);%360,390 -% fp.where('Runs', 'symbolrate','EQUALS', 162e9); -fp.where('Runs', 'fiber_length','EQUALS', 2); +fp.where('Runs', 'bitrate','EQUALS', 300e9);%360,390 +% fp.where('Runs', 'symbolrate','EQUALS', 195e9); +% fp.where('Runs', 'fiber_length','EQUALS', 1); 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','EQUAL', 1310); -fp.where('Runs', 'db_mode','EQUALS', 0); -fp.where('Runs', 'rop_attenuation','EQUAL', 0); +fp.where('Runs', 'db_mode','EQUALS', 1); +% fp.where('Runs', 'rop_attenuation','EQUAL', 0); % fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); @@ -66,15 +66,41 @@ 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, "serial", 'wh', wh, 'waitbar', true); +%% results_db = results(dataTable.db_mode==1); results_nodb = results(dataTable.db_mode==0); -for i = 1:numel(results_db) +%BER for ML based MLSE +for i = 1:length(results_db) + ber_m = cellfun(@(c) c.metrics.BER_precoded, results_db{1,i}.mlmlse_package); + [BER_MLMLSE_pre_emph(i), ~] = min(ber_m); + + ber_m = cellfun(@(c) c.metrics.BER_precoded, results_nodb{1,i}.mlmlse_package); + [BER_MLMLSE(i), ~] = min(ber_m); + + ber_m = cellfun(@(c) c.metrics.BER_precoded, results_nodb{1,i}.mlmlse_package); + [BER_MLMLSE(i), ~] = min(ber_m); + + baudrate(i) = dataTable.symbolrate(i); + rop_db(i) = dataTable.power_rop((2*i)-1); +end + +figure(11);hold on +plot(sort(rop_db),sort(BER_MLMLSE_pre_emph)) +plot(sort(rop_db),sort(BER_MLMLSE)) +beautifyBERplot + +%% +results_db = results(dataTable.db_mode==1); +results_nodb = results(dataTable.db_mode==0); + +for i = 1:numel(results_nodb) % VNLE (from results_nodb) gmi_v = cellfun(@(c) c.metrics.GMI, results_nodb{1,i}.vnle_package); @@ -89,9 +115,9 @@ for i = 1:numel(results_db) idx_air_max_vnle(i) = find(air_v == max(air_v), 1); % MLSE (from results_db) - gmi_m = cellfun(@(c) c.metrics.GMI, results_nodb{1,i}.mlse_package); + gmi_m = cellfun(@(c) c.metrics.GMI, results_db{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); + air_m = cellfun(@(c) c.metrics.AIR, results_db{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); @@ -111,11 +137,22 @@ for i = 1:numel(results_db) idx_gmi_min_db(i) = find(gmi_db == min(gmi_db), 1); idx_air_max_db(i) = find(air_db == max(air_db), 1); + %BER for ML based MLSE + ber_m = cellfun(@(c) c.metrics.BER, results_nodb{1,i}.mlmlse_package); + [BER_MLMLSE(i), idx_ber] = min(ber_m); + ber_m = cellfun(@(c) c.metrics.BER_precoded, results_db{1,i}.mlmlse_package); + [BER_MLMLSE_PREC(i), idx_ber] = min(ber_m); + % metadata bitrate(i) = dataTable.bitrate(i); baudrate(i) = dataTable.symbolrate(i); + rop_atten(i) = dataTable.rop_attenuation(2*i); + rop_pre(i) = dataTable.power_rop((2*i)-1); + rop(i) = dataTable.power_rop((2*i)); 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) @@ -128,6 +165,7 @@ cm.VNLE = cols(1 + d, :); cm.MLSE = cols(2 + d, :); cm.DB_precode = cols(3 + d, :); cm.DB = cols(4 + d, :); % duobinary +cm.ML_MLSE = cols(6 + d, :); % prepare x values in GBd xGHz = baudrate .* 1e-9; @@ -139,7 +177,7 @@ mk.VNLE = {'Marker','o','MarkerFaceColor',cm.VNLE,'MarkerEdgeColor',cm.VNLE,' 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}; - +mk.ML_MLSE = {'Marker','^','MarkerFaceColor',cm.ML_MLSE,'MarkerEdgeColor',cm.ML_MLSE,'MarkerSize',MARKER_SIZE}; % ---------------- FIGURE : BER ---------------- figure(112+M); clf; hold on; @@ -149,20 +187,100 @@ plot(xGHz, BER_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); + mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); +plot(xGHz, BER_MLMLSE_PREC, ... + 'DisplayName','ML-based MLSE (L=2)', ... + mk.ML_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)); +set(gca, 'XTick', xticks_vals(1:end), 'XTickLabel', xtick_labels(1:end)); grid on; legend('Location','best'); +beautifyBERplot + + + +%% +%% +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) + +% --- 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 +cm.ML_MLSE = cols(6 + d, :); + +% prepare x values in GBd +xdbm = flip(sort(rop)); +xticks_vals = xdbm; +xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false); + +% 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}; +mk.ML_MLSE = {'Marker','^','MarkerFaceColor',cm.ML_MLSE,'MarkerEdgeColor',cm.ML_MLSE,'MarkerSize',MARKER_SIZE}; + +% ---------------- FIGURE : BER ---------------- +figure(112+M); clf; hold on; +plot(flip(sort(rop)), sort(BER_VNLE), ... + 'DisplayName','VNLE', ... + mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); +plot(flip(sort(rop)), sort(BER_MLSE), ... + 'DisplayName','MLSE', ... + mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +plot(flip(sort(rop)), sort(BER_MLSE), ... + 'DisplayName','MLSE', ... + mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); +plot(flip(sort(rop_pre)), sort(BER_DB_PREC), ... + 'DisplayName','Diff. Precode + DB tgt.', ... + mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); +plot(flip(sort(rop_pre)), sort(BER_MLMLSE_PREC), ... + 'DisplayName','ML-based MLSE Diff. Prec. (L=2)', ... + mk.ML_MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.ML_MLSE); +plot(flip(sort(rop_pre)), sort(BER_MLMLSE), ... + 'DisplayName','ML-based MLSE (L=2)', ... + mk.ML_MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB_precode); + +yline(2e-2,'LineWidth',1,'HandleVisibility','off'); +yline(4.85e-3,'LineWidth',1,'HandleVisibility','off'); +yline(2.2e-4,'LineWidth',1,'HandleVisibility','off'); +xlabel('ROP in dBm'); +ylabel('BER'); +set(gca, 'yscale', 'log'); +% set(gca, 'XTick', xticks_vals(1:end), 'XTickLabel', xtick_labels(1:end)); +grid on; +legend('Location','best'); +beautifyBERplot + + +%% + + + + + + + + + + + + % ---------------- FIGURE 15 : GMI ---------------- diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m index 73bd3e6..4e1fa0c 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m @@ -6,31 +6,32 @@ M = 4; fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); % fp.where('Runs', 'pam_level','EQUALS', M); -% fp.where('Runs', 'symbolrate','EQUALS', 150e9); +% fp.where('Runs', 'symbolrate','EQUALS', 165e9); %150, 165, 180, 195, 210, 225, 240 % fp.where('Runs', 'fiber_length','EQUALS', 10); -fp.where('Runs', 'is_mpi','EQUALS', 0); +% 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); +fp.where('Runs', 'db_mode','EQUALS', 2); % 0 == high preemphasis // 1 == low preemphasis +% fp.where('Runs', 'rop_attenuation','EQUALS', 0); -fields = db.getTableFieldNames('power_state_info'); +fields = db.getTableFieldNames('power_state_info'); fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')]; [dataTable,~] = db.queryDB(fp, fields); %% cfg = struct; -cfg.x_axis = 'wavelength'; % 'symbolrate' | 'baudrate' | 'bitrate' | 'wavelength' -cfg.y_axis = 'power_mzm'; % 'BER' | 'GMI' | 'AIR' | ... -cfg.group_by = {}; -cfg.filters = struct('is_mpi',0,'pam_level',M,'equalizer_structure',[equalizer_structure.vnle,equalizer_structure.ffe,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle_db_mlse,equalizer_structure.dfe]); +cfg.x_axis = 'symbolrate'; % 'symbolrate' | 'bitrate' | 'wavelength' +cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ... +cfg.group_by = {'wavelength'}; +cfg.filters = struct('is_mpi',0,'pam_level',M,'equalizer_structure',[equalizer_structure.vnle_db_mlse]);%,equalizer_structure.ffe,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle_db_mlse,equalizer_structure.dfe]); cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available cfg.show_raw = true; -cfg.show_spread = 'none'; % 'none' or 'iqr' +cfg.show_spread = 'iqr'; % 'none' or 'iqr' cfg.agg = 'mean'; % or 'median' cfg.show_precoded = 0; cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional @@ -45,5 +46,4 @@ cfg.plot.scatterAlpha = 0.35; cfg.plot.legendLocation = 'best'; cfg.plot.fecLineWidth = 2.4; % thicker FEC limits - plot_measurements_gpt(dataTable, cfg); \ No newline at end of file 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..6543e9f --- /dev/null +++ b/projects/ML_based_MLSE/experimental_data.m @@ -0,0 +1,162 @@ + + +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 0 + [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 0000000000000000000000000000000000000000..cab08ae6305313b7f3a13116d0d032471f430465 GIT binary patch literal 14741 zcmaKzb8u$ey6t1zwr#$#la8H^?GE19wr$%sI<}LJZ9D0pbNf4WZ{595oqgu2HP>2I ztH!7~|9a~A&7mX*4uJvk&-i9w^ZmbG{_lhhLIPrEWp8C?WNTpNZe-_RYi3~SV&rCK z#q8n^4+0AD&wu|lsjDG^K+j{LTW$Wm5y6qHl>SQ;1_TC#+}q;cL`na%sIiH)lYxVg z9h2SvBv1BNmLC({iu~{M3UKALF_!S9n&Ou3oFXa=MH~2bs}tKQ+jkJ(Z$b@P4@t zqvthgXRr&Gw0Ax{e>A=+n*t&S zyc4m*{al2!N)}n}8sB9)=|D{Gf~f=W_>PCk5S>ZYQd-=ONd3HyQ#T;hr3m1KRqZClD$n$~6Q*?jiDb~#jb?51R zZoEE)MbV$^?>h}}=Nj-B#LNnTqLHwT)nVlyL+J0x4~TnPcpqbAjdL(KLI3$?{G$UQ ziPsT`Ug58(={#+#xLV(Y!hb1S?cOV`soZZ|cZ*$21-C1Plqn3LLOSs$?8C2bs>^VB zc44;IDOPQL7OpAIhY7@#2IJb-hN@ya8qRzWuRxoV<*cufuy)v?PynxGA?mR{;1BMq zB2bpO82|~EHMWfLb91S=UQqJQG~3ivwX8cqVtIm70HxV<%vqf=BZY-?=LH_UBk|;*2Pvn^LfY?Nsx^QM8574^@73^{o9w-6> z8qU@8Pd7NW^Qo#`0tRH%!%&i>-vBVjSz3=>H#z9kZ|JaW}YKi zj(tpPU71(D67`A*)16ovZc#cQso@)^8y$LS>Lqdm?Z8k5@CWmxCWA548 zj;&y$;OgY*xP|aaF{b?VO8$IEu;1=dAI$J^(j#bRrSo8ol;6mal345?h_vQlrHPQF zj%i?(Sd)xCiI%Lsm-z#hJ+*45drfOcY~fH+y*6#P+~yIsni*?Gv<8HYV(CxK;7DV( zRLjAp3f}HucKH#FWpySb(#@=@Qx>eNwK16FiiJf7L5b`CII!?}Izk}sS-aov!(SdC zh2EYa`7vr9R~|i9IO@zy!0vb4rf!}K0(DQ0lgu+XuudSBzmU8Pv>{MtFQ}686C9K; z*QN257dHtm3sUAuY}`I6v>?e_$y<{KWdn$)51M!3Jm``q2xL!Ta&;$fDaz~!Ov?*Q>H3SdFWBp_+=DL!1S*vI>p@HdS15Ca_zxUH&6pKLxN{~BrJFiJi>xwIraqRTo)UHZk!s`j*etu3(> zzV^LtS~#{WK$wU0B1d|wQ=pb=Sf3{h3SBUxe?@}6$W3eS3~!tax56;RSevfbi#xn} zQr^fS|8*1w_$qWSENkbZ@vUPd7fb!rT&r}GcfaQ<`wi)&2tvN{-Adg%VZR+13fIIa6_ykmhd2mG22}JHhYsCPTMVPv^rb>DMdxrP|o-wh5Qc2V0)2DLowIZ zy)YIMLI=($%+dPS?pPjjP$Oh4{Q(Ljc&WP(>HViTX`pK|o1)Q#*gr& ze3P{Y6#~oBXM0@dv#loU&W5xtw7iecx-W^%bMT~4(3u4^s@O()(2h966T<-i9CXVZ zsPyRlN!R1{KUQqJNu_q27CdIs z@J@&ep}3Y5y_gzpRWr<&U0o7>l6V4A*RTmFMO*xNGJhI3(^%Nt>>=}t{WUmhsf4PY zJ?;XQ*_!p62d{FVi@`O}ZE~8p9`Gk{&(1$=8X8pTph(pO48|gT^9IYcf$^m$|7C6B zK{DH$P+>^n=opx`n`{4#arykSVkH4xr*jwnawyIi<`!jS~JTz`JX5Cy~y3$jPB(H*c zCf^I?@aG$0L?n49frzoTlVD*FtN5I-WWQlcL=u{iAGXx1kctA*Zbwl&FTk8?!d!mW zUCJIRzg>P@t(}E@SZzELOVt!RIB`$#2X%R^SZeMrKjApMBfRKvZu{~Wm~A?RiSD0BIKr z7HK#&YMl-q8@#{$?N+neg`DQc)8h?~da;bzbhsjTktaDB@@vx0e07LV(H~;DH*k{- zGow~xn^t2=s!MmPy?Mx`bCWto!^2AKcWQc=^YRXX`Kg?!^|WI(L#%3pOg!vq(F_*!B{yJ z0mqB^m@s&;3|0$V#d~#E?ClcVIRuQc&}+X$EH_+!?!0VeYGfSEj3Kzyd`*@1B;I1m z0FBjHl*PFZz08^(2E#N0b~9<@i>>AK9+`m35GK+(Iz7qJ8qDR8COcUbt$bw+yTbEl zM$>kB6>dY5AvfBk4wxWRf#{%jgH$7y&|EdAxe&NabZ=>TP-_!e<8XQ9tmwO6sDI9r z*>IvAZ8^oPDcrvR83Mj-hVT!&Wv2N<{lYS70d)Jnqc|Y1WbhE59ahhYNGtrp)n=Tq7jdc>|imu+w)4EU) zPB^laz#Ej01r>h(3=U>4aK(Zddd!_xol3=yV$hq$j+rj5IN4dCYfheiQ6NOD66iL@ zXd$^eDLrr1;FooBq20paJCoa5+*>tg-#82YzOF~qR40~{v3rfGpO23O7gjqz4=Iqp z5rOe1;ZwF#B9lsOxeR{8X^&j{+Hv0!fHMBTZJngr#Q0fp(#~SHAwsO2&zyMldB|HL zzNh@-(yWcZC%O4757_)k?qD6?%{#N_poMRB;>vumT zu=eREd;B49hLAI&jPsp)3^()HC#_iY;jm+ac6-he-r0u|rI67?ZIqmQ1`mFbXbho6 z1FoT@If?75)iRfOP}|4n?YQl*MyJl>Zq`MS_$qIpfBhM6Ym=aCL05G_S1|-6)#44= zbL0@c2sIyXs&NRawmXm!(9lO6effQBi2(kh4)fIOKhdsMBdxrgoBn`#EX?1pr{?b--_XG+Te`=)g3`-&9 zdpk=|;KZOB*ndAY;hP4W)B6jb5tfQs#9zdcwb z1f?y{>)2Q~yVHhV^fYwI;Z1SqLkkXkvE%ZHYQwi@H{Y&)2rvZXA==Ay27D~9VJqxg zf+d3%?(PUCK-IFeW!9DEZ}`-E?s*4TN3l&(fC*DD$O^mh7l|R~i;C~hSf#Xi&98Tj zny6IJ&`EY}lI@$*qWHth6)v`36k1|Xsx=QH1qn3wrM^m(;NK>3v8c{LYv_nEch;f! zhh;V)2~rxhGf=#}`~9)YFkX{LH*YjWmqI`5Mj~++792O26S-C6&&Zwm zBJ5KXKR9t8Ohj)O!rKMkFtQ)*2`D8FVbYuonX|?M*-B#>U|WSsl{7{p;&FiOVAI}@ zR*_kXMFYRm(6-K*1O0t=iH6{4=|F>5qU$0xP{@PhUG_;1Z60JF1~bL$CAkbU3*0sR zx%{ilq*cfmdATz+C5!u0OoPbNB&^fqha9tD<$+rv9RZ`Z9!*gi(Y;Yw1Ck~IhTx}| zof`D1t;gv^SmBh|L3ucwaGu)P{pVrhm1Dpusm*!D%S-JNP~ZIOW)0m&XuSg9b16A5@j8^_~31&6GraLdvL zVS&o+;k+ew_1?<*S%PKDbZCd@bHFxQz11QbUW|@i3&tQa9XNz;MQGTbNmW8`+|RK7 zStNIBRB2B1Sp0Ew->DImqH#WT>__9Plkl^En86?7LcbC(B@pbQwu{!~ov#)W4N*XK zgrT~OV+mPGGn5|>e#4i9A>mdCMWaPfzQZtC%)zzSAYYb3i$iI!Vp%Eq@P8y11~VzS zh+`<_tW#4{nX3vkg!NjytXeu(iVg`o1iRS2&K&y=bz5HepK3&O#*Ac5p$@P5M*}ls z3_}@NIFJwE4$9QvR~WCe^5U02Pm!Jl_g$|PLcPO(@3>=P?y-TD733>9<0BrD5&a-; zQDd++1tP^e+dvq$K6X#M8%usuE2cY47Bfc(oOW^NV-J+VpanyFR?k)F-hNCtBDs9t zw`cYsz(t-WmN%7TD5o4fe8_bU533);>$SjlT@8TZV%JN6}t8qet3lUVBw5rLey1|W4Lgt-QT*KzqYLnq!AgI2f-&R+f9w;aSvT~R7uU%4M`FGTu&?STw{UM@E-QP*D zBgIfVAKX1dZhHYVRWquFf+4o6Ttn8rf~bA2bEVEa@kknOBT4 zx6m1OeTLl=pP@Wnv8+g2{jgyzf7T(A768vW`?n=eXL&k>8_L!N;TbLojo+nlXElRQ zF$k&!@_sQXiXaOP_aJA~Y^TT<(}d5lt>Q@}k0J-v4GKZQyi*xG4W#xF{7~A;krt~i zG!S&6H+aAWEVts7jjh6*+WzSQRkX3-PbnS>ep>{n53(E+3B6Q%QQDv?T^&(cZJd%K zX4y4lbQ*7FgcqqsmNK%GFTv-7!(bjSi}PJ640g1ttMJ4C{X?- z);qov1+XpmR(;m(1o3?QxR2-+$ETMyKR9~=Ob_MUIfm|(w(GmFIKx29JH=QgpiNTy z?4uVaIm!@sBqP>9sLE_~=%mw}O1j~iXIMl3q#|Kl>t#A3c5z?RT?q_)eZG9U=`|*1 z7SwHudf3U;%l8T&~-Drxf>9=@Z? zC#D#1cm{Ydt}4M0@j(jPC0LFG=JEb!gHJLxe^MBJhn;%?y@0$%drv>Mr9O?QRby}p zVN#zxqpVN9eVTdbDED;<1;09jvZKJe>oBUgpuqdIq_EcBxiZBo!UvfdP?Rmxn9<`N zE}2RbEYvj%X6`)*jki*Flh{;kiy+&Dv++$GvdeX+raonSZ63I_s^U@amH70SzN zl?Gik`;Bpj-?L*up`VT<<`b$~rytR=`nx+N9R4o0??%vb&mp};BJRR5miChVjfS%?yn20d#6X(*1_1&Vf*K|{hV0#;^2blCtTO4S$9O*>caCUFxz z&i{6*FDRs5Rbmw&1@-oB`=yFLABrt6bUOP5rX&XqgQqyipN9hif~W@q^8aawj{LtP z7MlM|L;f3i`JbT(;tQJ9$Y16{@)wFMYia*DXvOkHj{g$IVWAjKHXe7xTXhJ?P|*2i zWC5ieso!2f`aQZvawFWSb}Q`T*$WBV&h;2ekBE+hO(OTikUL4#vfDEG^ikFIQ|k z9DI5YBOdF@LO%TScKrwBp)la+36%y`K~X``KX@oyC{nADiL3Ou@sX+3E@{^HdbHY* zcT^ElafzMQjm^X+njvpzS~?S&ftt#B1&OkJAw{%%?_KMz!3N5IZJbj=E_H!M!R^#VvfO?gqt2qnUX~amfHQ2{SxOXqW_yEckD_m^5Zv zGg;rDN`pz9pctevdWv|s;K>0~MoL7S3AmBXS6ue*(NHdDAMI#Ct&Br)uYL3@k$JW)ADYj~3(;-5Zt8d^zD=OT__lqoF~UeW^tI zK6g0sDXI^nj*bF|!iF_63V(tmqr zHRX$KSts!+5Ue$wZMn4ULi=h2Guxv60Btge%5v`tH^!*FX&-#^ZVnfDhkS2@eE68D zS32drEm||u?pzmsJtpM4RQ<%wS84|LjKnOt+2(sGKj6Ux2okR1pOwI12qj4QMMi>J zf{e1q)`!6?B0X7B8!GR)<(C4jJzXuYimXaKDhQZG%T=R$1y`2n)m<9_daN_k0s^@} z4}r?{{2|_|VGTyoSF#;Wl`8RyMvy$~$3Jga_0#E2@Hk0_ZhCxU7vkhPhuLEsN;WR# zWej0~kEVG35HNb9CWmQ5`7WkbMT3SqwZ4-!h2AgZWV^-DLCC~E_<`UGtEH1HcMqRr zTUcwYr%K_G+F>)6WnO3)N?}Cfi<#9AX($U;xEk9T3O~+}o8m-effy4GOWbOj zOO=<+=oE%k9EK1ih}-vYQ=mM(eVDWhrr>}=ip5dXJrp_y?FMeL!kV$X*zit@Q;~ix z&LZ&EHyw9>6+eJrF<~Za;a%3kt}MpUmztsV>Bu~Z{uWa+-5y?KzEupj6-GSA@2_b& zuJOX={&P9{*93_eWYSqOKR=J^tMvLVV`9)a*1*1-1)6EZOkQKdGbTIY-*WX-nN$7aId8pP0XK_DWDI0fh;IXg zWaa^pO#`}h-ly`uy+N-50^y(uW8SL)$tlaPiY@82`1CJ(=N^&#KHvJ8v|~T>@`J{< zIcfS7llGC|Xufrx2frmDEsmtDeYKhyeYU6RP=R;Wr6VZ+$s9+*{z_cVZ5@Z(U~p=7 z30`;b3=GoYkIWmZdY4voQK1wvPPT3K^q`A+5PLd+CR+3z@2f^UKX^CuMGp;1*wnVfT;8=BlHLkNpKE-iHtcc&i(>(U%EFI8u9#Dm%l;I1T~WUEAalJOx9cZcm7NvN?=O?%nAkY3&ei z%b_*75=3l%Ey@#vH4Aa?2m2$pvHEmmEHrteaS~60v zB*&Bq`~v-_G?)X=!H=KtaVs%?zMJJ7ATkmD!Fb97)4`U z`fA%}*w*N0)@S6O#ObNZ0SS=gk!LajHdxamb!}}Aj8mxC4U(PMy{QN?QkDLnx7;6e zSo?yQTp!PIUwm$xCB(4ZC&dZGTe155D(<&Tm_$RALWg8dklABX$T?GS!{d* zk^nL2d*Tj9JCX|ixVlsSCGOcgLv*gtsm&5iO=@I`9Vz;ulv%$^1I{FzCkNPDqq+2}w@ATEr~ zEQ~pVX`V|!nqvXqv?h^#Lv`7e5+l8YX3XlL6s5VJPRjq5@^w6dIq$m=ZH|#9qWA+m zM={{qK-VH zqeNA>#=3OQJ0iIz4O*|YacZQPY$B4MUP}dg1_uFC9ja_aG+?BFX7?p*2k4aiE{GAUnGgK4{b=*^K=HnEAP&vssfgbe`UnR# z@4#gf*LjBW3lnIysj>?ZJjCI_sX@Oa978YAm$1b02$t^P#?yV^EGKdC#c4K4FJq{0 zQOew4T;w*`tthK6b1WQzQHM$wmReow11LtosOO622=O3j3r1y5TtRUgBLeHGJyT;P z0TL;36J28=tbnG_kb`|Dm{jV(DcnSgDY&z^<>BztR`f9(`89b+NMe&$EbIu85`QEk zwgzQ;Nr|YdTlV8So;3?|hFb{ly?^HXLp{Tjs@FvK1mvR~niuBlNXxfM)Z2Q`Xc_%-2MGRq53Z}hy z#xa=x9^k1L8Goouj%J-5==RJa(4Y$R-*R~RiHmUkw15ox_Rzu=iZ?@|&3E+cU~zIi zZH??U>0Tj~jL=|a&YqpL%H6@E-T1oQja`w#1x@p4G)m%k#!1fyo}N|qa7>#e-yZ;q zy3C*lr14%ETh_RXR)%ny#|aU*e%+l*>M;Axcg`Co2gYAFLTam|H@=Ju^`x#$F!=Pv zf*M=*-rPa!63QaU1?)f7`2^h(X*VNUC|#-r2zh9fR>TxuE+cf*8-09=eXl6IJ5a zlxg+DuFkwdt9#TjVj?vn8gXaMp}PTvMctu9{v|+ZTvWnNxtTR zk@KrKwDzAkFs9kM^SC}SqF(%)!o{XueEX?Gklvqjk|VTNKu+iZ_B~}9EaSQ0pA}Kc z#)%}(S$vhq08?)bqTi<@)dvMV2h!jCx^uXm8m0IlIM?voxp1GcJ)y;|T%XDgNIyKs zR_EeubtZ%0Mym<+`sVhMvEea?7`+wD9eUMh57wZH7`Zt72a!87S9c%xAKxBNFCTYe zYZd!?Uss1RUnPrByV)CBH?vc#dU;u;neOA+t6g~Fz+tnrnEswb3;Fd6UO)`mMdMtg z_UMoY`|?*ZGmc(Q*8sp`)tryO-V#l7XK|+q&uK{EEYCKW5)uqO*AfGd^ufbLiKKo; zUm>)fr9LY_wPL{rQ`e;~+4C_3$SSGN7qP0=B+&3#L8S~&B67Dbm;9?bV0~k)%8L)!w(>kJ-9AF$TEaNNHm0_eGAMA~>xM|u!$ONF9bFom-(5=WtNuUR(nzW8UOL*l;AS-;YxaGdl%jus^|6EvD8 zCh^fVmQ1+Q02cb4i&n}jLvjI4u7A`Hk6lgiIcnCjWOsfad<7{$dX0g*Pv+iqYaoB9 zoii>J*k5s}`qs!Xv0!)(D<#Q}qh@+x4BoM(v5()=4=Xi4k4XNSa!2O3n0csV58F9s ze+j?zw5yZX&{N89_B8WyP2YfP@=-4QY+m&7E#0`UQ^6+Ek^57?WK-Vpy=>8t_BGJoe()NJ^kqzC-huRN5~G&kw>E`IA8KFrFca-GH<* zf)$&=HjlD0Np<|fCE#Z}b2@Ea$ULYdRA)_zjvSYHppA8Zivp*D;qghORWw~bl6HbR zz%bh1J{ZBGk$au{v!4rqS0$1n$q7E)q2C;FTA8|pZB4g7xgyE3wJ zi!VK9vjqDOjc_<(8C!?Gs3i0fwDa0Tu3Yro>G_a13#iF&lUssiIK!^UzXyro^w-f7 z=Cr5Pg!SD&mQkpu!Kxa3SwY!Suz7)!3^ zibU#!x^2}d_1qwppg{zSzqK9?88$zbp*CYkPI0n8Q!e2x%NYrD@#oJJHc5_h8E|Js zFHh#(0`X!l5)b5ZG2RBm__bbM=uO&!W}Pd|Y8hHoKE@sJ-V)TS#W!}UeKY7CyV-k2 zo-bi}*H?Pvg7mTS$M(u~FZ9G#+$ct4*4x7|uA+x(IfuWZHqzYRdm0C$hOHN(Y<%4M zhkGT>XK*@C?rBIhKT_HE!DT^tH>|Quu?Yo|!~`$BwtxtA%I_0onqH!lnykJG72IiOL~ALe2ujFXNa zq|yO;E|c1z`n37!B#8kb+_6Lfae{$@?=Zku)L!LL9GyZr9~X@iDwflob_}!1V7}7C z5K`6iO}OwVRbj$0mn9i}EN&PXQp4}0K~d{@`ra!YQ%F>UpWVN;5A80dWV?N$$WG z!fQkRw1J^Hecj}=4f8y2W_Q%0HmyO55e;=5fO+nZUalZ@2J2*JIyCzMEsyH^qs(7jePK2p=kR2E|!&K$4;*8!PD> z^E5y79YGn}A7j&!6uxWv!$m-(HqOSvnJ)^5nUuai=p}N3dW;{2xqe2o`4JOGkBrRa z5507cfR0iHaR9cNH|@d6B(gc$zA{87C!vM1Z6;yZ_yU3z+bFn$Pvpz=Of8T|zAB(j zkE+wO$W0mn))RKeM5W{*t#(bjfNp2j(aG||8oIEHbSL|iqQ;n~h@`j0A&Sfd0LWIO zaWz`Q8ZNhcVC|y1Lmy!+<)IYvI_MdXl9x2t4`S|hpC`C0D3C{0utn_Zd_CGusDGUU zK`WC?qEMf((I!9y*Fw&$)80iRBj7| zIlvZoiE7u>jaH?eD1pC7JRUl_Axfai}ojYe8RA+ zePcqSwsM`%N;`Y2`jQHzWkX@ZNtyYM9hH`}S9$dMfzC@f=UoYfN;Nk!ef5HvZ?m^! zJY;n@(t)UP{6G=Te!=dwK&(f1TjIb{DqUU7&81R*Dh+OQ)h&0>v3d3cA}{$&JPT7h zDdk)^L7`}Jw2ZDr3jbO>^;lcX$6o3FjOuabwPG?N5XW-8%9yW|n`vw1mcN)G2?oaeB5TaPe9n+-HQz$Dd2C8Z?xy4s%FWefyj5Q6d0YJq=8e zua9jg&&oh>080*q8&C~unWT(Jj^$L2Z7)rVgCv^l=5EqZAAX+4JthEmfs2oG^9)3) zPO`{*SaIQ3j4t(%)lT?-1zkQ+f%nMcL;R*2=|IxBgh!r~X`}v37)RmPRc0oU1fY zd>fiOt~A;;ynbUFfC*jW$zcL^z88Sc;E7Rd(xUm?8j(T2FuWA6iAvmy z0KTW8Dpo;F%Vkd&z*}8OeimN+iYI(TlL0(vzHkYsG?rb8n^Tx?@sT3!@3_|3iNhIQ zOkTk^k<<0#K|C#WaW5DkuEdFZAFxP*OWFL{<|{50zW2xK(nk=WweU{m_z1xiMHXxR zNQ;Ig5ikm;eWCdBv(95)$f#cyc@Fv^x3pcz__jyg8T<{m3}J=b;%L&UXnC7_Jad<{ zzaehE+I3{Y3eoPWXQ{M>-0gN2EXZ^3b8+GV{Wj6>wO=~?+Oimy(~>AbH#?bxvd0hArI#4I-KP zQOAJ*<*t{6R5<&IYn0MSq!O_riV^5txeRz-`+X5C(kZi%K2z1bCeX3-xjpo??msm! zC8@_*C&SQx9{m1kG9Ps*8a`lxfFvvY(_{t(!vOzJA-}(n`u~nEk$+zP8+iVYkv~Wf z(0?L-|4;n?h5h~&e3OFwfAQ7;_5UpXzeNB3+WZs!`&XNPDEhxP(P{q8#sv32uHnBe z?SEkEf8G9J>i_EYPhAw5 literal 0 HcmV?d00001 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/rate_evaluation.m b/projects/ML_based_MLSE/rate_evaluation.m index 07e4057..00494b7 100644 --- a/projects/ML_based_MLSE/rate_evaluation.m +++ b/projects/ML_based_MLSE/rate_evaluation.m @@ -5,14 +5,14 @@ ber_dbtgt = []; ber_ml = []; mlse = 1; -dbtgt = 1; - +dbtgt = 0; +duob_mode = db_mode.no_db; baudrates = [136:8:224].*1e9; -parfor i = 1:length(baudrates) +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",1300,"link_length_m",0,"random_key",1,"apply_pulsef",0); + [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); @@ -25,6 +25,7 @@ parfor i = 1:length(baudrates) 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, ... @@ -33,6 +34,9 @@ parfor i = 1:length(baudrates) 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 @@ -69,11 +73,14 @@ parfor i = 1:length(baudrates) %% RUN ML-Based MLSE mu_lms = 0.15; - ml_mlse_equalizer = ML_MLSE("epochs_tr",30,"epochs_dd",1,"len_tr",2^15,... - "mu_dd",mu_lms,"mu_tr",mu_lms,"order",5,"sps",1,... - "traceback_depth",128,"L",3,"delta",0); + 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); - [y_ml_mlse,~] = ml_mlse_equalizer.process(y_white,Symbols_v1); + 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)); 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 index 68ed3eb..eb77a3f 100644 --- a/projects/ML_based_MLSE/rop_evaluation.m +++ b/projects/ML_based_MLSE/rop_evaluation.m @@ -1,95 +1,139 @@ +clear; clc; -ber_ffe = []; -ber_mlse = []; -ber_dbtgt = []; -ber_ml = []; +M = 4; +randkey = 1; +duob_mode = db_mode.no_db; mlse = 1; dbtgt = 1; -rops = linspace(-15,-5,12); -parfor i = 1:length(rops) - - rop = rops(i); - M = 4; - [Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1] = standard_link_model("M",M,"fsym",224e9,"rop",rop,"laser_linewidth",1300,"link_length_m",0,"random_key",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; - end +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 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 - - %% RUN ML-Based MLSE - - mu_lms = 0.15; - ml_mlse_equalizer = ML_MLSE("epochs_tr",30,"epochs_dd",1,"len_tr",2^14,... - "mu_dd",mu_lms,"mu_tr",mu_lms,"order",4,"sps",2,... - "traceback_depth",128,"L",2,"delta",0); - [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)]) + %% 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 -%% - -figure(3); hold on; -if mlse -plot(rops,ber_ffe,'DisplayName','FFE'); -plot(rops,ber_mlse,'DisplayName','MLSE'); -end -if dbtgt -plot(rops,ber_dbtgt,'DisplayName','DB tgt'); -end -plot(rops,ber_ml,'DisplayName','ML-MLSE'); -beautifyBERplot; -legend - - - - - - - +%% ====================== 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 index 2412af1..004c249 100644 --- a/projects/ML_based_MLSE/standard_link_model.m +++ b/projects/ML_based_MLSE/standard_link_model.m @@ -22,7 +22,7 @@ function [Rx_sig_2sps,Symbols,Tx_bits] = standard_link_model(options) options.laser_linewidth (1,1) double = 1e6 % --- Channel parameters --- - options.link_length_m (1,1) double = 0 + 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 @@ -57,7 +57,7 @@ function [Rx_sig_2sps,Symbols,Tx_bits] = standard_link_model(options) "randomkey",options.random_key+1).process(El_sig); % --- Fiber --- - Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",options.link_length_m, ... + 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) --- @@ -90,6 +90,10 @@ function [Rx_sig_2sps,Symbols,Tx_bits] = standard_link_model(options) [~,Scpe_cell,~,found_sync] = Scpe_sig_2sps.tsynch( ... "reference",Symbols,"fs_ref",options.fsym,"debug_plots",0); - Rx_sig_2sps = Scpe_cell{1}.normalize("mode","rms"); + 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; + From 888cbbd23e372e2bc14c63d1344b57404e8d1700 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Fri, 14 Nov 2025 11:45:13 +0100 Subject: [PATCH 28/30] Simulation preps for high speed. --- Classes/00_signals/Signal.m | 4 +- Classes/04_DSP/Equalizer/ML_MLSE.m | 493 +++++++----------- Functions/EQ_structures/dsp_runid.m | 11 +- Functions/EQ_structures/duobinary_signaling.m | 22 +- Functions/EQ_structures/ml_mlse.m | 55 +- .../EQ_structures/vnle_postfilter_mlse.m | 23 +- Functions/Job_Processing/preprocessSignal.m | 2 +- .../Auswertung_JLT/run_dsp_from_db.m | 289 ++++------ .../Auswertung_JLT/run_plot_measurements.m | 20 +- 9 files changed, 384 insertions(+), 535 deletions(-) diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index 159b4e3..228e0cd 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -965,8 +965,8 @@ classdef Signal maxA = max(sig(100:end-100))*1.3; minA = min(sig(100:end-100))*1.3; - maxA = 0.0025; - minA = 0; + % maxA = 0.0025; + % minA = 0; difference= maxA-minA; diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index 4539f62..239c2d8 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -1,44 +1,16 @@ classdef ML_MLSE < 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 + % --------------------------------------------------------------------- + % 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 % usually 2 + sps order e e_tr @@ -48,27 +20,23 @@ classdef ML_MLSE < handle mu_tr epochs_tr - dd_mode % 1 or 0 to set DD-mode on or off - mu_dd %weight update in dd mode + dd_mode + mu_dd epochs_dd - adaptive_mu constellation - - L %viterbi memory length - + L alpha DIR DIR_flip trellis_states - traceback_depth + delta - % --- Added internal class variables used later --- + % Internal variables S Nf - delta nStates nFeasible combs @@ -79,38 +47,32 @@ classdef ML_MLSE < handle valid_from_idx w - % --- New: fast state lookup --- + % Fast lookup + nSym + key_table + trans_index true_to_state_idx - state_dict % containers.Map: key(sequence)->state index - key_fmt = '%.8g_'; % key format for sequence strings - nSym % |constellation| + % Debug metrics ber = [] - ce = ones(1,1); + 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; + 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 - + options.L = 1; end fn = fieldnames(options); @@ -122,13 +84,12 @@ classdef ML_MLSE < handle obj.error = 0; end + % ============================================================== + % PROCESS + % ============================================================== function [X,X_viterbi] = process(obj, X, D) - - % actual processing of the signal (steps 1. - 3.) - % 1 normalize RMS + % Normalize input RMS X = X.normalize("mode","rms"); - - % Use sorted constellation for deterministic mapping obj.constellation = sort(unique(D.signal),'ascend'); obj.nSym = numel(obj.constellation); @@ -136,22 +97,17 @@ classdef ML_MLSE < handle warning('Signal length does not fit to reference!'); end - % ============================================================== - % INITIALIZATION (only before final epoch and detection mode) - % ============================================================== - % --- Parameters - obj.S = numel(obj.constellation); % alphabet size - obj.Nf = obj.order*obj.sps; % filter length - % obj.delta = 3;%ceil(obj.Nf/2); % delay parameter + obj.S = obj.nSym; + obj.Nf = obj.order * obj.sps; obj.nStates = obj.S^obj.L; - obj.nFeasible = obj.nStates*obj.S; + obj.nFeasible = obj.nStates * obj.S; % --- 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{:}).'); % rows: states, columns: [x_k, x_{k-1}, ...] + 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); @@ -165,328 +121,235 @@ classdef ML_MLSE < handle end end end - [obj.valid_to_idx, obj.valid_from_idx] = find(obj.valid); + [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 fitler-set + % --- Initialize weights if isempty(obj.w) || any(size(obj.w) ~= [obj.Nf+1,obj.nFeasible]) - obj.w = zeros(obj.Nf+1,obj.nFeasible); % filter weights per transition + bias tap obj.w = randn(obj.Nf+1,obj.nFeasible); end - % --- 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}, ...] + % --- 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 - obj.state_dict = containers.Map(keys, 1:obj.nStates); % ============================================================== - % TRAINING + % TRAINING % ============================================================== - - % Training Mode - n = obj.len_tr; - training = 1; - obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,n,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; % ============================================================== - % DD-Mode / Fixed Mode + % DECISION-DIRECTED / TESTING % ============================================================== - - % 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); + 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.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 + % ============================================================== + % EQUALIZE + % ============================================================== function [y,y_ref] = equalize(obj,x,d,mu,epochs,N,training) - % ============================================================== - % FFE + Whitening + ML-Based Branch Metric Estimation + Viterbi - % ============================================================== debug = 1; showPlots = 1; - - % --- Input padding and preallocation y = zeros(N,1); - - % number of symbol steps in this block nSymbols = ceil(N/obj.sps); for epoch = 1:epochs - - % state metrics (log-domain costs): keep as column [nStates×1] - pm = zeros(obj.nStates,1); % v_{k-1}(s′) - c_hat = zeros(1,obj.nFeasible); - v_tilde = zeros(1,obj.nFeasible); - pred = zeros(nSymbols, obj.nStates, 'uint32'); - pm_sto = nan(obj.nStates, nSymbols,'like',pm); + pm = zeros(obj.nStates,1); + pred = zeros(nSymbols,obj.nStates,'uint32'); + pm_sto = nan(obj.nStates,nSymbols,'like',pm); CE_accum = 0; - - %%% START IDX - if 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 - start_sample = 1; - end_sample = start_sample + (ceil(N/obj.sps)-1)*obj.sps; - else - start_sample = 1;%obj.len_tr; - end_sample = N; - end - - start_symbol = 1 + floor((start_sample - 1)/obj.sps); % ABSOLUTE symbol index + 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); % [d_k-L+1 ... d_k] - true_to_state_idx = obj.state_dict(obj.seq_key(flip(init_seq))); % [d_k ... d_k-L+1] + 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 - % Not enough history – fall back to state 1 true_to_state_idx = uint32(1); end - symbol = 0; for sample = start_sample:obj.sps:end_sample - symbol = symbol + 1; - k = symbol; + symbol = (sample - start_sample)/obj.sps + 1; sym_idx = start_symbol + (symbol - 1); - % --- Build Δ-delayed observation window y_k - i1 = sample - obj.Nf + 1 + obj.delta; - i2 = sample + obj.delta; + % --- 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)]; % Nf×1 - yk = [yk;1]; + yk = [zeros(padL,1); buf(:); zeros(padR,1)]; + yk = [yk;1]; - % --- Predict branch metrics for all feasible transitions: c_hat - c_hat = (yk.' * obj.w); % [1×nFeasible] - c_hat = c_hat.'; % [nFeasible×1] - - % --- Extended path metrics: v_tilde = pm(from) + c_hat - % normalize pm to avoid growth (invariant to additive const) + % --- Branch metrics + c_hat = (yk.' * obj.w).'; pm = pm - min(pm); - v_tilde = pm(obj.valid_from_idx) + c_hat; % [nFeasible×1] + v_tilde = pm(obj.valid_from_idx) + c_hat; - % ===== Gradient update (Algorithm 1) ===== + % --- allocate once + if epoch==1 && symbol==1 + obj.true_to_state_idx = ones(ceil(N/obj.sps),1,'uint32'); + end - 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 "from" + if symbol>1 + true_from_state_idx = obj.true_to_state_idx(symbol-1); + else + true_from_state_idx = 1; + 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; + % --- 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 - end - - % --- reuse cached state from second epoch onward - true_to_state_idx = obj.true_to_state_idx(symbol); - - % --- ensure valid (from,to) - dirac = zeros(obj.nFeasible,1); - mask = obj.valid_from_idx==true_from_state_idx & ... - obj.valid_to_idx ==true_to_state_idx; - if any(mask) - dirac(mask) = 1; + obj.true_to_state_idx(symbol) = state_idx; 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); + 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 - - - % 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 (≈ exp(50)=3e21) + % =================================================================== + % 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); + p = expv./(sum(expv)+eps); + CE_symbol(symbol) = -log(p(dirac==1)+eps); - % for logging only: - 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); + % --- CE smoothing and adaptive μ + if sym_idx>obj.L + CE_smooth(symbol)=0.01*CE_symbol(symbol)+0.99*CE_symbol(symbol-1); else - if epoch > 1 - CE_smooth(symbol) = obj.ce(end); %use ce from last epoch or =1 for very first round?! - else - CE_smooth(symbol) = CE_symbol(symbol); - end + CE_smooth(symbol)=CE_symbol(symbol); end + CE_accum=CE_accum+CE_symbol(symbol); - CE_accum = CE_symbol(symbol) + CE_accum; - - - % gradient term (t - p) - dmp = (dirac - p)'; % 1×nFeasible - - % Per-feature gradient; implicit expansion gives (Nf+1)×nFeasible - dL_Dw = (yk) .* dmp; - - % Start updates only when the ABSOLUTE symbol index has ≥ L history - if sym_idx >= obj.L + % --- Gradient update + dmp=(dirac-p)'; + dL_Dw=(yk).*dmp; + 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); + mu_eff=CE_smooth(symbol); + mu_eff=max(min(mu_eff,0.2),1e-4); else - mu_eff = mu; + mu_eff=mu; end - - obj.w = obj.w - mu_eff .* dL_Dw; % (Nf+1)×nFeasible + obj.w=obj.w - mu_eff.*dL_Dw; 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 (matrix form, min of costs) - v_tilde_mat = inf(obj.nStates, obj.nStates); - v_tilde_mat(obj.valid) = v_tilde; - [pm_next, pred(k,:)] = min(v_tilde_mat, [], 2); - - % re-center to keep metrics bounded (decision-invariant) - pm_next = pm_next - min(pm_next); - - pm = pm_next; - pm_sto(:,symbol) = pm; + % =================================================================== + % 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 (full; you can window with traceback_depth if desired) - [~, s_end] = min(pm); - viterbi_path = zeros(symbol,1,'uint32'); - viterbi_path(symbol) = s_end; - for n = symbol:-1:2 - viterbi_path(n-1) = pred(n, viterbi_path(n)); + % --- 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(viterbi_path); - - 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))); + 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(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; + 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 - ser = err./length(y); - fprintf('Epoch: %d - SER: %.1e \n',epoch, ser); + fprintf('Epoch %d - SER: %.2e\n',epoch,ser); + obj.ber(epoch)=ser; end - - obj.ce(epoch) = CE_accum./symbol; + obj.ce(epoch)=CE_accum/symbol; - if showPlots + if debug && mod(epoch,10)==1 && showPlots figure(10);clf subplot(3,2,1:2); - heatmap(obj.w); - title('Filter') - + imagesc(obj.w);axis xy;colorbar;title('Filter W'); subplot(3,2,3); - v_tildemat = NaN(obj.nStates, obj.nStates); - v_tildemat(obj.valid) = v_tilde; % log-domain scores - heatmap(v_tildemat); - title('Path Metrics (v_tilde)') - + 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); - scatter(1:symbol,pm_sto,1,'.') - title('Path Metric Winners') - - subplot(3,2,5);hold on + 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 - - % Left y-axis: Cross Entropy (linear) + 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') - - % Right y-axis: BER (logarithmic) + 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 scale)') - - xlim([1, epochs]) - xlabel('Epoch') - title('Cross Entropy // BER') - grid on - - drawnow + 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 - 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); + % ============================================================== + % 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/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m index 976bcfa..086daf4 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -97,10 +97,10 @@ try use_ffe = 0; use_dfe = 0; - use_vnle_mlse = 0; + use_vnle_mlse = 1; use_dbtgt = 0; use_dbenc = 0; - use_ml_mlse = 1; + use_ml_mlse = 0; addProcessingResultToDatabase = 0; @@ -140,7 +140,7 @@ try % Preprocess signal Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym); - % Scpe_sig.spectrum("fignum",2223,"normalizeTo0dB",1,"displayname",'Rx'); + Scpe_sig.spectrum("fignum",2223,"normalizeTo0dB",1,"displayname",'Rx'); % Scpe_sig.spectrum("fignum",22233,"normalizeTo0dB",0,"displayname",'Rx'); % Scpe_sig.eye(fsym,M,"fignum",1024); @@ -194,7 +194,8 @@ 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; @@ -288,6 +289,8 @@ 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 c484918..7f13c35 100644 --- a/Functions/EQ_structures/duobinary_signaling.m +++ b/Functions/EQ_structures/duobinary_signaling.m @@ -35,8 +35,26 @@ 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); +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); diff --git a/Functions/EQ_structures/ml_mlse.m b/Functions/EQ_structures/ml_mlse.m index abd3aa0..f2d468d 100644 --- a/Functions/EQ_structures/ml_mlse.m +++ b/Functions/EQ_structures/ml_mlse.m @@ -44,31 +44,35 @@ try 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.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; +% 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 @@ -111,3 +115,26 @@ switch precode_mode [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/Job_Processing/preprocessSignal.m b/Functions/Job_Processing/preprocessSignal.m index 4828bba..428c84a 100644 --- a/Functions/Job_Processing/preprocessSignal.m +++ b/Functions/Job_Processing/preprocessSignal.m @@ -16,7 +16,7 @@ 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, ... +Scpe_sig = Filter('filtdegree', 8, "f_cutoff", Symbols.fs.*0.52, ... "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ... "active", true).process(Scpe_sig); 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 212a72f..6df7d7f 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 = 2; +dsp_options.append_to_db = 0; +dsp_options.max_occurences = 15; experiment = "highspeed_2024"; dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' @@ -39,19 +39,19 @@ end % === Get Run ID's === fp = QueryFilter(); -% fp.where('Runs', 'run_id','EQUALS', 987); +% fp.where('Runs', 'run_id','EQUALS', 2776); M = 4; fp.where('Runs', 'pam_level','EQUALS', M); -fp.where('Runs', 'bitrate','EQUALS', 300e9);%360,390 +% fp.where('Runs', 'bitrate','EQUALS', 300e9);%360,390 % fp.where('Runs', 'symbolrate','EQUALS', 195e9); -% fp.where('Runs', 'fiber_length','EQUALS', 1); -fp.where('Runs', 'is_mpi','EQUALS', 0); +fp.where('Runs', 'fiber_length','EQUALS', 2); +% 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','EQUAL', 1310); fp.where('Runs', 'db_mode','EQUALS', 1); -% fp.where('Runs', 'rop_attenuation','EQUAL', 0); +fp.where('Runs', 'rop_attenuation','EQUAL', 0); % fp.where('Runs', 'power_pd_in','GREATER_THAN', 7); [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs')); @@ -72,203 +72,115 @@ wh.addStorage("mlmlse_package"); [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true); -%% -results_db = results(dataTable.db_mode==1); -results_nodb = results(dataTable.db_mode==0); -%BER for ML based MLSE -for i = 1:length(results_db) - ber_m = cellfun(@(c) c.metrics.BER_precoded, results_db{1,i}.mlmlse_package); - [BER_MLMLSE_pre_emph(i), ~] = min(ber_m); - ber_m = cellfun(@(c) c.metrics.BER_precoded, results_nodb{1,i}.mlmlse_package); - [BER_MLMLSE(i), ~] = min(ber_m); - ber_m = cellfun(@(c) c.metrics.BER_precoded, results_nodb{1,i}.mlmlse_package); - [BER_MLMLSE(i), ~] = min(ber_m); - - baudrate(i) = dataTable.symbolrate(i); - rop_db(i) = dataTable.power_rop((2*i)-1); + + +%% ========================================================================= +% LOAD METADATA +% ========================================================================= +[dataTable, ~] = db.queryDB(fp, db.getTableFieldNames('Runs')); +results = results(:).'; % ensure row vector +N = numel(results); + +% ========================================================================= +% 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); + +% ========================================================================= +% EXTRACT METRICS (ONE LOOP, ROBUST) +% ========================================================================= +for i = 1:N + r = results{i}; + + % ---- 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 + + % ---- 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 -figure(11);hold on -plot(sort(rop_db),sort(BER_MLMLSE_pre_emph)) -plot(sort(rop_db),sort(BER_MLMLSE)) -beautifyBERplot +%% ========================================================================= +% METADATA (ALWAYS INDEX-ALIGNED WITH RESULTS) +% ========================================================================= +bitrate = dataTable.bitrate(:).'; +baudrate = dataTable.symbolrate(:).'; -%% -results_db = results(dataTable.db_mode==1); -results_nodb = results(dataTable.db_mode==0); +rop_atten = dataTable.rop_attenuation(2:2:end).'; +rop_pre = dataTable.power_rop(1:2:end).'; +rop = dataTable.power_rop(2:2:end).'; -for i = 1:numel(results_nodb) +% ========================================================================= +% PLOT STYLE +% ========================================================================= +STYLE_BASE = 2; +MARKER_SIZE = STYLE_BASE; +LINE_WIDTH = max(2, STYLE_BASE/3); - % 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); +cols = cbrewer2('Paired', 8); - % MLSE (from results_db) - gmi_m = cellfun(@(c) c.metrics.GMI, results_db{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_db{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); +cm.VNLE = cols(1,:); +cm.MLSE = cols(2,:); +cm.DB_PREC = cols(3,:); +cm.DB = cols(4,:); +cm.ML_MLSE = cols(6,:); - % 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); +mk = @(col,shape) {'Marker',shape,'MarkerFaceColor',col,'MarkerEdgeColor',col,'MarkerSize',MARKER_SIZE}; - 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); - - %BER for ML based MLSE - ber_m = cellfun(@(c) c.metrics.BER, results_nodb{1,i}.mlmlse_package); - [BER_MLMLSE(i), idx_ber] = min(ber_m); - ber_m = cellfun(@(c) c.metrics.BER_precoded, results_db{1,i}.mlmlse_package); - [BER_MLMLSE_PREC(i), idx_ber] = min(ber_m); - - % metadata - bitrate(i) = dataTable.bitrate(i); - baudrate(i) = dataTable.symbolrate(i); - rop_atten(i) = dataTable.rop_attenuation(2*i); - rop_pre(i) = dataTable.power_rop((2*i)-1); - rop(i) = dataTable.power_rop((2*i)); -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) - -% --- 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 -cm.ML_MLSE = cols(6 + d, :); - -% prepare x values in GBd -xGHz = baudrate .* 1e-9; -xticks_vals = xGHz; -xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false); - -% 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}; -mk.ML_MLSE = {'Marker','^','MarkerFaceColor',cm.ML_MLSE,'MarkerEdgeColor',cm.ML_MLSE,'MarkerSize',MARKER_SIZE}; - -% ---------------- FIGURE : BER ---------------- +% ========================================================================= +% 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_PREC, ... - 'DisplayName','Diff. Precode + DB tgt.', ... - mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); -plot(xGHz, BER_MLMLSE_PREC, ... - 'DisplayName','ML-based MLSE (L=2)', ... - mk.ML_MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.ML_MLSE); +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:end), 'XTickLabel', xtick_labels(1:end)); -grid on; -legend('Location','best'); -beautifyBERplot - - - -%% -%% -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) - -% --- 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 -cm.ML_MLSE = cols(6 + d, :); - -% prepare x values in GBd -xdbm = flip(sort(rop)); -xticks_vals = xdbm; -xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false); - -% 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}; -mk.ML_MLSE = {'Marker','^','MarkerFaceColor',cm.ML_MLSE,'MarkerEdgeColor',cm.ML_MLSE,'MarkerSize',MARKER_SIZE}; - -% ---------------- FIGURE : BER ---------------- -figure(112+M); clf; hold on; -plot(flip(sort(rop)), sort(BER_VNLE), ... - 'DisplayName','VNLE', ... - mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); -plot(flip(sort(rop)), sort(BER_MLSE), ... - 'DisplayName','MLSE', ... - mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); -plot(flip(sort(rop)), sort(BER_MLSE), ... - 'DisplayName','MLSE', ... - mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); -plot(flip(sort(rop_pre)), sort(BER_DB_PREC), ... - 'DisplayName','Diff. Precode + DB tgt.', ... - mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); -plot(flip(sort(rop_pre)), sort(BER_MLMLSE_PREC), ... - 'DisplayName','ML-based MLSE Diff. Prec. (L=2)', ... - mk.ML_MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.ML_MLSE); -plot(flip(sort(rop_pre)), sort(BER_MLMLSE), ... - 'DisplayName','ML-based MLSE (L=2)', ... - mk.ML_MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB_precode); - -yline(2e-2,'LineWidth',1,'HandleVisibility','off'); -yline(4.85e-3,'LineWidth',1,'HandleVisibility','off'); -yline(2.2e-4,'LineWidth',1,'HandleVisibility','off'); -xlabel('ROP in dBm'); -ylabel('BER'); -set(gca, 'yscale', 'log'); -% set(gca, 'XTick', xticks_vals(1:end), 'XTickLabel', xtick_labels(1:end)); -grid on; -legend('Location','best'); -beautifyBERplot - - -%% +set(gca, 'YScale', 'log'); grid on; legend('Location','best'); +% beautifyBERplot; @@ -283,7 +195,10 @@ 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/Auswertung_JLT/run_plot_measurements.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m index 4e1fa0c..739f73f 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m @@ -5,16 +5,16 @@ db = DBHandler("dataBase", [dataBase], "type", database_type); M = 4; fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 987); -% fp.where('Runs', 'pam_level','EQUALS', M); +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', 10); +fp.where('Runs', 'fiber_length','EQUALS', 10); % 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', 2); % 0 == high preemphasis // 1 == low preemphasis +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'); @@ -25,15 +25,15 @@ fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')]; cfg = struct; cfg.x_axis = 'symbolrate'; % 'symbolrate' | 'bitrate' | 'wavelength' cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ... -cfg.group_by = {'wavelength'}; -cfg.filters = struct('is_mpi',0,'pam_level',M,'equalizer_structure',[equalizer_structure.vnle_db_mlse]);%,equalizer_structure.ffe,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle_db_mlse,equalizer_structure.dfe]); +cfg.group_by = {'wavelength','equalizer_structure','pre_emph'}; +cfg.filters = struct('is_mpi',0,'pam_level',M,'equalizer_structure',[equalizer_structure.ml_mlse,equalizer_structure.vnle,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle_db_mlse,equalizer_structure.dfe]); cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available cfg.show_raw = true; -cfg.show_spread = 'iqr'; % 'none' or 'iqr' +cfg.show_spread = 'none'; % 'none' or 'iqr' cfg.agg = 'mean'; % or 'median' -cfg.show_precoded = 0; +cfg.show_precoded = 1; cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional % New styling knobs @@ -46,4 +46,6 @@ cfg.plot.scatterAlpha = 0.35; cfg.plot.legendLocation = 'best'; cfg.plot.fecLineWidth = 2.4; % thicker FEC limits -plot_measurements_gpt(dataTable, cfg); \ No newline at end of file +plot_measurements_gpt(dataTable, cfg); + +% beautifyBERplot() \ No newline at end of file From 75dddca1f210c95cd1f26951ebebf871e675ec71 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Fri, 21 Nov 2025 15:37:58 +0100 Subject: [PATCH 29/30] Stuff during JLT writing... --- Classes/00_signals/Signal.m | 3 + Classes/01_transmit/ChannelFreqResp.m | 25 +- Classes/Warehouse_class/classes/DataStorage.m | 16 +- Datatypes/clr.m | 17 -- Functions/EQ_structures/dsp_runid.m | 21 +- Functions/Job_Processing/preprocessSignal.m | 2 + Functions/Lab_helper/loadFreqResp.m | 2 +- .../Theory/Neuer Ordner/duobinary_histogram.m | 51 ---- .../Neuer Ordner/duobinary_transferfunction.m | 18 -- Functions/beautifyBERplot.m | 19 +- .../Auswertung_JLT/ber_vs_rate.m | 13 +- .../ber_vs_rate_only_best_configs.m | 2 +- .../final/compare_ber_best_results.m | 223 ++++++++++++++++++ .../final/compare_ber_complete_results.m | 170 +++++++++++++ .../final/compare_pre_emphasis.m | 112 +++++++++ .../final/generate_spectrum_plots.m | 100 ++++++++ .../{ => final}/plot_measurements_gpt.m | 151 +++++++++--- .../Auswertung_JLT/final/plot_rates.m | 34 +++ .../Auswertung_JLT/final/rates.csv | 38 +++ .../{ => final}/run_plot_measurements.m | 49 +++- .../Auswertung_JLT/run_dsp_from_db.m | 8 +- .../auswertung/only_show_precomp.m | 5 +- .../bias_evaluation.m | 8 +- projects/ML_based_MLSE/wpd_datasets.csv | 9 + projects/standard_system/freqresp_test.m | 39 ++- 25 files changed, 946 insertions(+), 189 deletions(-) delete mode 100644 Functions/Theory/Neuer Ordner/duobinary_histogram.m delete mode 100644 Functions/Theory/Neuer Ordner/duobinary_transferfunction.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_best_results.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_complete_results.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_pre_emphasis.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/generate_spectrum_plots.m rename projects/HighSpeedExperiment_2024/Auswertung_JLT/{ => final}/plot_measurements_gpt.m (73%) create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_rates.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/rates.csv rename projects/HighSpeedExperiment_2024/Auswertung_JLT/{ => final}/run_plot_measurements.m (59%) create mode 100644 projects/ML_based_MLSE/wpd_datasets.csv diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index 228e0cd..7eaa1c0 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -348,6 +348,7 @@ classdef Signal options.displayname = ""; options.color = []; options.normalizeToNyquist = 0; + options.addDCoffset = 0; options.normalizeTo0dB = 0; options.max_num_lines = []; % Leave empty or omit to disable line rotation options.fft_length = []; @@ -415,6 +416,8 @@ classdef Signal ax = gca; hold on + p_dbm = p_dbm+options.addDCoffset; + for s = 1:min(size(p_dbm)) if isempty(options.color) plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1); diff --git a/Classes/01_transmit/ChannelFreqResp.m b/Classes/01_transmit/ChannelFreqResp.m index 0c9fda8..cfffe17 100644 --- a/Classes/01_transmit/ChannelFreqResp.m +++ b/Classes/01_transmit/ChannelFreqResp.m @@ -206,7 +206,7 @@ classdef ChannelFreqResp < handle function plot(obj) - figure(55); + figure(); clf; Havg = obj.H; @@ -225,7 +225,7 @@ classdef ChannelFreqResp < handle xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on; - figure(56); + figure(); clf; @@ -246,7 +246,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 +269,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/Warehouse_class/classes/DataStorage.m b/Classes/Warehouse_class/classes/DataStorage.m index 93a7cf4..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/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/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m index 086daf4..210728a 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -95,12 +95,12 @@ try adaption= 1; use_dd_mode = 1; - use_ffe = 0; - use_dfe = 0; + use_ffe = 1; + use_dfe = 1; use_vnle_mlse = 1; - use_dbtgt = 0; - use_dbenc = 0; - use_ml_mlse = 0; + use_dbtgt = 1; + use_dbenc = 1; + use_ml_mlse = 1; addProcessingResultToDatabase = 0; @@ -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); @@ -239,10 +244,10 @@ try if use_ml_mlse %ML-based MLSE (L=2) - mu_ml = 0.01; training_epochs = 250; + 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",2,"delta",4,"adaptive_mu",0); + "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; diff --git a/Functions/Job_Processing/preprocessSignal.m b/Functions/Job_Processing/preprocessSignal.m index 428c84a..ed40a6c 100644 --- a/Functions/Job_Processing/preprocessSignal.m +++ b/Functions/Job_Processing/preprocessSignal.m @@ -16,9 +16,11 @@ 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 +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); +end % Remove DC offset Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); 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/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/beautifyBERplot.m b/Functions/beautifyBERplot.m index 2d70cee..0697abc 100644 --- a/Functions/beautifyBERplot.m +++ b/Functions/beautifyBERplot.m @@ -23,8 +23,8 @@ num_markers = length(markers); % --- style all lines consistently for i = 1:length(lines) - lines(i).LineWidth = 1.1; - lines(i).LineStyle = '-'; + lines(i).LineWidth = 1; + % lines(i).LineStyle = '-'; if string(lines(i).Marker) == "none" lines(i).Marker = markers{mod(i-1, num_markers) + 1}; end @@ -88,10 +88,23 @@ 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', 'Times New Roman'); +set(gca, 'FontSize', 10, 'FontName', 'Latin Modern Roman'); + end diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/ber_vs_rate.m index 1c39b25..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 = 4; +M = 6; fp.where('Runs', 'pam_level','EQUALS', M); % fp.where('Runs', 'bitrate','LESS_THAN', 310e9); fp.where('Runs', 'fiber_length','EQUALS', 2); @@ -18,7 +18,7 @@ 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_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/compare_ber_best_results.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_best_results.m new file mode 100644 index 0000000..b38a814 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_best_results.m @@ -0,0 +1,223 @@ + + +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; +tp = TransmissionPerformance; +netrates_vnle = tp.calculateNetRate(baudrate.* m, ... + '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; +% 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; +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_ber_complete_results.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_complete_results.m new file mode 100644 index 0000000..5cd72da --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_complete_results.m @@ -0,0 +1,170 @@ +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_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 = '-'; + + +%% Grid configuration +rows = 3; % PAM 4,6,8 +cols = 4; % DSP schemes +pam = [4 6 8]; + +cfg.figure_number = 46; +fig = figure(cfg.figure_number); clf; + +% Create extremely compact tile layout +t = tiledlayout(rows, cols, ... + 'TileSpacing','compact', ... + 'Padding','compact'); + +% Common cfg +cfg.show_precoded = 1; +cfg.group_by = {'equalizer_structure','pre_emph'}; +cfg.x_axis = 'grossrate'; +cfg.y_axis = 'BER'; +cfg.y_scale = 'log'; +cfg.plot.custom_colors_scatter = []; +cfg.plot.use_cbrewer2 = false; +cfg.fec_lines = []; +% DSP scheme 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; + + +% === Main nested loop: rows = PAM format, columns = DSP scheme === +for r = 1:rows + M = pam(r); + + for c = 1:cols + ax = nexttile(t, (r-1)*cols + c); + cfg.ax = ax; + + % ---- pre-emph = 1 (solid) ---- + cfg.plot.custom_colors = DSP(c).lightcolor; + cfg.plot.custom_linetypes = {'-'}; + cfg.filters = struct('is_mpi',0,'pam_level',M, ... + 'equalizer_structure',DSP(c).eq, ... + 'pre_emph',1); + plot_measurements_gpt(dataTable, cfg); + + % ---- pre-emph = 0 (dashed) ---- + cfg.plot.custom_colors = DSP(c).color; + cfg.plot.custom_linetypes = {'-'}; + cfg.filters.pre_emph = 0; + plot_measurements_gpt(dataTable, cfg); + + if M == 4 + ylim([1e-5 0.2]); + else + ylim([6e-4 0.2]); + end + + % % FEC limits + yline([2.2e-4 4.85e-3 2e-2],'LineWidth',1.1,'Color',[0.2 0.2 0.2], ... + 'LineStyle',':','HandleVisibility','off'); + + % Axes prettification + beautifyBERplot; + + % ===== Remove redundant labels ===== + if c > 1 + ax.YLabel = []; + end + if r < rows + ax.XLabel = []; + end + + grid(ax,'on'); box(ax,'on'); + end +end + +%this is just a ranom size but fits and is fixed now! +pos = 1e3.*[0.0983 0.6110 1.4113 0.4733]; +set(fig, 'Position', pos) + + + + +%% === 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/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/generate_spectrum_plots.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/generate_spectrum_plots.m new file mode 100644 index 0000000..29ec0c4 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/generate_spectrum_plots.m @@ -0,0 +1,100 @@ +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 0 + %% 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(); +end \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt.m similarity index 73% rename from projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m rename to projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt.m index d0b95a1..fc88501 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/plot_measurements_gpt.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt.m @@ -14,13 +14,13 @@ defaults = struct( ... 'filters' , struct, ... 'agg' , 'mean', ... 'outlier' , 'auto', ... - 'mad_z' , 3, ... + 'mad_z' , 4, ... 'pct_limits' , [2.5 97.5], ... 'min_pts_x' , 3, ... 'show_raw' , true, ... 'show_precoded', [], ... 'show_spread' , 'none', ... - 'fec_lines' , [2.2e-4 4.85e-3 2e-2], ... + 'fec_lines' , [], ... 'plot', struct() ... ); cfg = filldefaults(cfg, defaults); @@ -75,6 +75,11 @@ 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 @@ -92,8 +97,29 @@ nG = max(G); % ==== Colors (cbrewer2 'Paired' with dark/ light pairs) ==== [cols_line, cols_scatter] = buildGroupColors(nG, cfg.plot); -%% ---- Plotting -figure; hold on; grid on; +%% ---- 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); @@ -120,34 +146,32 @@ for gi = 1:nG 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); end + 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); + yu = yu(ord); + ylo = ylo(ord); + yhi = yhi(ord); % Styles % Decide if we style by pre_emph - canStyleByPre = cfg.plot.use_pre_emph_styling && ismember('pre_emph', T.Properties.VariableNames); + % --- LINE TYPE SELECTION (no pre-emphasis logic) --- + ls = cfg.plot.lineStyle_default; - if canStyleByPre - if any(strcmp(group_by,'pre_emph')) - % pre_emph is an explicit grouping key -> take it from the group table - pre = logical(grpTbl.pre_emph(gi)); - else - % pre_emph not grouped, but available in the rows -> infer from the members of this group - pre = logical(mode(T.pre_emph(G==gi))); - end - ls = tern(pre, cfg.plot.lineStyle_pre_emph_on, cfg.plot.lineStyle_pre_emph_off); - else - % no pre-emph styling → use a single default style - 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); @@ -156,12 +180,12 @@ for gi = 1:nG colL = cols_line(gi,:); h.lines(gi) = plot(xu, yu, ... 'LineWidth', cfg.plot.lineWidth, ... - 'Marker', cfg.plot.marker, 'MarkerSize', 5, ... + 'Marker', cfg.plot.marker, 'MarkerSize', 3, ... 'Color', colL, 'LineStyle', ls, ... 'DisplayName', char(lbl)); % Spread (IQR) in line color - if strcmpi(cfg.show_spread,'iqr') && any(isfinite(ylo)) && any(isfinite(yhi)) + 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; @@ -195,30 +219,38 @@ for gi = 1:nG 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); end + 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', 5, ... + '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; +% 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-4, 0.3]); + ylim([1e-5, 0.5]); + yticks([1e-5, 1e-4, 1e-3, 1e-2, 1e-1]); end + + end % ===== main ===== @@ -237,7 +269,11 @@ end end function out = tern(cond, a, b) -if cond, out = a; else, out = b; end + if cond + out = a; + else + out = b; + end end function T2 = applyFilters(T, filters) @@ -274,9 +310,12 @@ switch lower(whichX) if ~ismember('pam_level', T.Properties.VariableNames) error('bitrate requires "pam_level" column.'); end - bits = log2(double(T.pam_level)); + bits = floor(log2(double(T.pam_level))*10)/10; x = (T.symbolrate .* bits) * 1e-9; - label = 'Bitrate [Gb/s]'; + 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); @@ -317,38 +356,78 @@ 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,'pre-emph on','pre-emph off'); end - parts(i) = sprintf('%s=%s', key, string(val)); + 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) -% Build paired colors (dark for lines, light for scatter) using cbrewer2('Paired') + +% --- 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); % ensure pairs available + 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, :); + dark = C(2*i-1, :); + light = C(2*i, :); else - light = C(2*i-1, :); dark = C(2*i, :); + light = C(2*i-1, :); + dark = C(2*i, :); end cols_line(i,:) = dark; cols_scatter(i,:) = light; end else - % Fallback: lines() + lightened scatter 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.5); % 50% toward white + cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.50); end end + end function c2 = lightenColor(c, fracTowardWhite) diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_rates.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_rates.m new file mode 100644 index 0000000..29b09b5 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_rates.m @@ -0,0 +1,34 @@ + +path = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\HighSpeedExperiment_2024\Auswertung_JLT\final\rates.csv"; + +%% === Read CSV into a table === +T = readtable(path, 'Delimiter', ';', 'DecimalSeparator', ','); + +%% === Column definitions === +x = T.X_Werte; +pam_cols = {'PAM_2','PAM_4','PAM_6','PAM_8','PAM_12'}; +titles = {'PAM-2','PAM-4','PAM-6','PAM-8','PAM-12'}; + +% === Create subplots === +figure;hold on +c = linspecer(6); +for k = 1:numel(pam_cols) + + y = T.(pam_cols{k}); + valid = ~isnan(y); + + + % Scatter plot + s = scatter(x(valid), y(valid), 25, 'filled','MarkerEdgeColor',c(k,:),'MarkerFaceColor',c(k,:),'DisplayName',titles{k}); + + % Fit (2nd-order polynomial) + p = polyfit(x(valid), y(valid), 2); + xfit = linspace(min(x(valid)), max(x(valid)), 200); + yfit = polyval(p, xfit); + + % Plot fit curve + plot(xfit, yfit, 'LineWidth', 1,'linestyle',':','Color',c(k,:),'HandleVisibility','off'); + + xlabel('baud rate [GBd]'); + ylabel('net rate [Gb/s]'); +end \ No newline at end of file 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/run_plot_measurements.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/run_plot_measurements.m similarity index 59% rename from projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m rename to projects/HighSpeedExperiment_2024/Auswertung_JLT/final/run_plot_measurements.m index 739f73f..6ebf0fc 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/run_plot_measurements.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/run_plot_measurements.m @@ -2,40 +2,59 @@ 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 = 4; +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', 10); +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', '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_new')]; +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 = 'symbolrate'; % 'symbolrate' | 'bitrate' | 'wavelength' +cfg.x_axis = 'grossrate'; % 'symbol rate' | 'bitrate' | 'wavelength' grossrate cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ... -cfg.group_by = {'wavelength','equalizer_structure','pre_emph'}; -cfg.filters = struct('is_mpi',0,'pam_level',M,'equalizer_structure',[equalizer_structure.ml_mlse,equalizer_structure.vnle,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle_db_mlse,equalizer_structure.dfe]); +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 = true; -cfg.show_spread = 'none'; % 'none' or 'iqr' -cfg.agg = 'mean'; % or 'median' -cfg.show_precoded = 1; +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'; @@ -48,4 +67,10 @@ cfg.plot.fecLineWidth = 2.4; % thicker FEC limits plot_measurements_gpt(dataTable, cfg); -% beautifyBERplot() \ No newline at end of file +% beautifyBERplot() + +%% FIG PRE EMPHASIS + + + + 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 6df7d7f..b5fdd4a 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 = 0; -dsp_options.max_occurences = 15; +dsp_options.max_occurences = 5; experiment = "highspeed_2024"; dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' @@ -42,15 +42,15 @@ fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 2776); M = 4; fp.where('Runs', 'pam_level','EQUALS', M); -% fp.where('Runs', 'bitrate','EQUALS', 300e9);%360,390 +fp.where('Runs', 'bitrate','EQUALS', 390e9);%360,390 % fp.where('Runs', 'symbolrate','EQUALS', 195e9); fp.where('Runs', 'fiber_length','EQUALS', 2); % 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','EQUAL', 1310); -fp.where('Runs', 'db_mode','EQUALS', 1); +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','GREATER_THAN', 7); diff --git a/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m b/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m index 2a8ee51..db72ac3 100644 --- a/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m +++ b/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m @@ -61,7 +61,7 @@ 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,... + "fsym",fsym,"M",M,"order",19,"useprbs",0,... "fs_out",fdac,... "applyclipping",0,"clipfactor",1.2,... "applypulseform",pulsef,"pulseformer",Pform,... @@ -87,4 +87,5 @@ Digi_sig= Digi_sig.normalize("mode","rms"); Digi_sig.spectrum("displayname","Strong Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0); - +ylim([-30,3]); +xlim([-5,100]); \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/bias_evaluation.m b/projects/HighSpeedExperiment_2024/bias_evaluation.m index 3fe8d9d..43e5b7e 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)); 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/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/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]; From 569e72a1fe50dc2c3d264140728ee3e05841d151 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Mon, 15 Dec 2025 15:04:15 +0100 Subject: [PATCH 30/30] Add new minimal example (+ a ton of other, not so important, changes) --- Classes/00_signals/Signal.m | 19 +- Classes/01_transmit/ChannelFreqResp.m | 8 + Classes/04_DSP/Equalizer/ML_MLSE.m | 4 +- Classes/DataBaseHandler/Metricstruct.m | 10 +- .../fwm_plots/automate_JLT_plots.m | 10 +- Functions/EQ_structures/dsp_runid.m | 10 +- Functions/EQ_structures/duobinary_target.m | 4 + Functions/EQ_visuals/showEQNoisePSD.m | 1 - Functions/EQ_visuals/showLevelHistogram.m | 2 + Functions/Job_Processing/preprocessSignal.m | 14 +- Functions/Theory/dispersion_10km.m | 103 ++--- Functions/beautifyBERplot.m | 13 +- Libs/wesanderson_colors/WesPalette.m | 133 ++++++ Libs/wesanderson_colors/hex2rgb.m | 20 + .../minimal_example_wespalette.m | 64 +++ Libs/wesanderson_colors/wes_palettes.m | 79 ++++ .../Auswertung_JLT/final/Copy_of_FIGURE_ROP.m | 146 ++++++ .../final/Copy_of_FIGURE_WAVELENGTH.m | 272 +++++++++++ .../Auswertung_JLT/final/FIGURE_BER_3x4.m | 132 ++++++ .../Auswertung_JLT/final/FIGURE_EYES.m | 257 +++++++++++ .../Auswertung_JLT/final/FIGURE_NGMI.m | 221 +++++++++ .../Auswertung_JLT/final/FIGURE_NGMI_v2.m | 180 ++++++++ .../Auswertung_JLT/final/FIGURE_ROP.m | 153 ++++++ .../Auswertung_JLT/final/FIGURE_Spectra.m | 182 ++++++++ .../Auswertung_JLT/final/FIGURE_WAVELENGTH.m | 124 +++++ .../final/FIGURE_WAVELENGTH_VS_BAUDRATE.m | 160 +++++++ .../final/FIGURE_introduction.m | 88 ++++ .../final/HighSpeedExperiments.xlsx | Bin 0 -> 26210 bytes .../final/analyze_measurements_gpt.m | 334 ++++++++++++++ .../final/compare_ber_best_results.m | 21 +- .../final/compare_ber_complete_results.m | 170 ------- .../Auswertung_JLT/final/example_usage.m | 92 ++++ .../final/generate_spectrum_plots.m | 15 +- .../final/plot_measurements_gpt.m | 358 +++----------- .../final/plot_measurements_gpt_old.m | 436 ++++++++++++++++++ .../Auswertung_JLT/final/plot_rates.m | 34 -- .../Auswertung_JLT/run_dsp_from_db.m | 23 +- .../a_minimal_example.m | 118 +++++ .../auswertung/only_show_precomp.m | 171 ++++--- .../bias_evaluation.m | 2 +- projects/IMDD_base_system/simulation_bwl_2.m | 80 +++- projects/ML_based_MLSE/experimental_data.m | 3 +- projects/WDM/WDM_model.m | 2 +- 43 files changed, 3594 insertions(+), 674 deletions(-) create mode 100644 Libs/wesanderson_colors/WesPalette.m create mode 100644 Libs/wesanderson_colors/hex2rgb.m create mode 100644 Libs/wesanderson_colors/minimal_example_wespalette.m create mode 100644 Libs/wesanderson_colors/wes_palettes.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_ROP.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/Copy_of_FIGURE_WAVELENGTH.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_BER_3x4.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_EYES.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_NGMI.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_NGMI_v2.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_ROP.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_Spectra.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_WAVELENGTH.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_WAVELENGTH_VS_BAUDRATE.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/FIGURE_introduction.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/HighSpeedExperiments.xlsx create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/analyze_measurements_gpt.m delete mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_complete_results.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/example_usage.m create mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt_old.m delete mode 100644 projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_rates.m create mode 100644 projects/HighSpeedExperiment_2024/a_minimal_example.m diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index 7eaa1c0..a21e71c 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -347,8 +347,10 @@ classdef Signal 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 = []; @@ -361,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, ... @@ -374,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 @@ -418,11 +424,18 @@ classdef Signal 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 @@ -968,8 +981,8 @@ classdef Signal maxA = max(sig(100:end-100))*1.3; minA = min(sig(100:end-100))*1.3; - % maxA = 0.0025; - % minA = 0; + maxA = 0.12; + minA = -0.08; difference= maxA-minA; diff --git a/Classes/01_transmit/ChannelFreqResp.m b/Classes/01_transmit/ChannelFreqResp.m index cfffe17..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 diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index 239c2d8..3f584c3 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -164,8 +164,8 @@ classdef ML_MLSE < handle % EQUALIZE % ============================================================== function [y,y_ref] = equalize(obj,x,d,mu,epochs,N,training) - debug = 1; - showPlots = 1; + debug = 0; + showPlots = 0; y = zeros(N,1); nSymbols = ceil(N/obj.sps); diff --git a/Classes/DataBaseHandler/Metricstruct.m b/Classes/DataBaseHandler/Metricstruct.m index f95311c..ef00675 100644 --- a/Classes/DataBaseHandler/Metricstruct.m +++ b/Classes/DataBaseHandler/Metricstruct.m @@ -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/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_JLT_plots.m b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_JLT_plots.m index 801c0e8..c29a009 100644 --- a/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_JLT_plots.m +++ b/Classes/Warehouse_class/functions/phase_predist_plots/fwm_plots/automate_JLT_plots.m @@ -21,7 +21,7 @@ height = 200; plotJob.Position = [100 100 width 100+height]; cols = cbrewer2("paired",12); plotJob.color = cols(1,:); -plotJob.l = 2; +plotJob.l = 10; plotJob.ch = 16; plotJob.d = 0; plotJob.sgm = 0; @@ -30,7 +30,7 @@ plotJob.p_in = 3; plotJob.gamma = 0.0023; plotJob.pmd = 0.1; plotJob.channelspacing = 400e9; -plotJob.randzdw = 0; +plotJob.randzdw = 1; plotJob.plot_ber_curve = 0; @@ -52,7 +52,7 @@ plotJob.yAxisLabel = 'BER'; % createbercurves(wh,plotJob) - P = [3,6]; + P = [3]; for i = 1:2 plotJob.p_in = P(i); createviolinplots(wh,plotJob); @@ -234,8 +234,8 @@ Title = ["Co Pol.","Link Segmentation","Paired Pol. Interl.","Alternating Pol. I D = [0,3,0,0]; Sgm = [0,1,0,0]; -colidx = [3]; -Len = [10]; +colidx = [4]; +Len = plotJob.l; fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName); if isvalid(fig) diff --git a/Functions/EQ_structures/dsp_runid.m b/Functions/EQ_structures/dsp_runid.m index 210728a..4d207bd 100644 --- a/Functions/EQ_structures/dsp_runid.m +++ b/Functions/EQ_structures/dsp_runid.m @@ -95,11 +95,11 @@ try adaption= 1; use_dd_mode = 1; - use_ffe = 1; - use_dfe = 1; - use_vnle_mlse = 1; - use_dbtgt = 1; - use_dbenc = 1; + use_ffe = 0; + use_dfe = 0; + use_vnle_mlse = 0; + use_dbtgt = 0; + use_dbenc = 0; use_ml_mlse = 1; addProcessingResultToDatabase = 0; 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_visuals/showEQNoisePSD.m b/Functions/EQ_visuals/showEQNoisePSD.m index 8eeed21..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 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/preprocessSignal.m b/Functions/Job_Processing/preprocessSignal.m index ed40a6c..6932697 100644 --- a/Functions/Job_Processing/preprocessSignal.m +++ b/Functions/Job_Processing/preprocessSignal.m @@ -16,13 +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 -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); +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/Theory/dispersion_10km.m b/Functions/Theory/dispersion_10km.m index 87a17a8..83fcf29 100644 --- a/Functions/Theory/dispersion_10km.m +++ b/Functions/Theory/dispersion_10km.m @@ -2,11 +2,11 @@ % IM/DD Fading Notch – λ_null vs. Bandwidth (Fixed 10 km) % ============================================================ - +clear; clc; %% Fiber and dispersion parameters -lambda0 = 1315e-9; % Zero-dispersion wavelength [m] -S0 = 0.08; % Dispersion slope at ZDW [ps/(nm²·km)] +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'); @@ -17,74 +17,73 @@ 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 % ------------------------------------------------------------ -% figure('Color','w'); -% plot(lambda_nm,f_GHz, 'LineWidth', 2); -% grid on; box on; - cols = cbrewer2('Paired',10); -figure('Color','w');hold on +figure('Color','w'); hold on; -plot(lambda_nm, f_GHz, 'LineWidth',2,'DisplayName',sprintf('%d km',L),'Color',cols(2,:)); +hLine = plot(lambda_nm, f_GHz, ... + 'LineWidth', 2, ... + 'DisplayName', sprintf('L = %.1f km', L/1000), ... + 'Color', cols(2,:)); -yticks([56,75,90,112]) - -f_GHz = [56,75,90,112] * 1e9; -[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_GHz, L, lambda0, S0); -lambda_nm = lambda_vec * 1e9; % Convert to nm - -xticks(round(lambda_nm)) - -xlabel('$\Delta \lambda$ from ZDW [nm]'); -ylabel('$F_{null}$ [GHz]'); +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]; + +lim = (lambda0.*1e9) - [8, 40]; xlim([lim(2) lim(1)]); -% ylim([40,130]) +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 -% 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³) + c = physconst('lightspeed'); + S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³) -% Define O-band boundaries [m] -lambda_min = 1260e-9; -lambda_max = 1360e-9; + lambda_min = 1260e-9; + lambda_max = 1360e-9; -f_target = f_target(:); -N = numel(f_target); + f_target = f_target(:); + N = numel(f_target); -lambda_vec = zeros(N,1); -Dacc_vec = zeros(N,1); + lambda_vec = zeros(N,1); + Dacc_vec = zeros(N,1); -for k = 1:N - RHS = c * 0.5 / (f_target(k)^2 * L); + 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; + fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS; - % Solve within normal-dispersion range - try - lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]); - catch - lambda_sol = lambda_min; + 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 - - % Clamp to O-band - 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 - Dacc_val = min(max(Dacc_val, -100), 100); - - Dacc_vec(k) = Dacc_val; -end end diff --git a/Functions/beautifyBERplot.m b/Functions/beautifyBERplot.m index 0697abc..9bc5d0a 100644 --- a/Functions/beautifyBERplot.m +++ b/Functions/beautifyBERplot.m @@ -11,6 +11,7 @@ function beautifyBERplot(options) 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 @@ -23,13 +24,15 @@ num_markers = length(markers); % --- style all lines consistently for i = 1:length(lines) - lines(i).LineWidth = 1; + lines(i).LineWidth = 1.2; % lines(i).LineStyle = '-'; - if string(lines(i).Marker) == "none" - lines(i).Marker = markers{mod(i-1, num_markers) + 1}; + 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 - lines(i).MarkerSize = 4; - lines(i).MarkerFaceColor = lines(i).Color; end % --- optional smoothing/fitting overlay 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/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 0000000000000000000000000000000000000000..59d1ab509026f1099bc19cd39d470e6bd700e11e GIT binary patch literal 26210 zcmeFYW1DTwmMxkqZQHhO+beC`#!B0^ZQHhO+r~=g$^GuC`TzXlB2>_nMC-I#otpHpC2gQ9s0eah;xcMvVBT-U4TtoP9u77q(#gI7Mavgd z=p#o1Vm5#vuO1!|D>yL*tYqiXBhtusq98d(?d<0y1@B}ETcg@v;ZBk`OH23X#)6$G ziuS{w_2qsuz} z*2fUEF*AR3cAaF3;jnioHrddEZu;aB3qyHeWWbVIdXmj0Z#bR{ z-3)N3t-ZWXg|*z?a+{);Xv}e?F0QKkYg=(-jqeOUn=AIn-Hi%zGP>X0AL!Hux$Y$1 z2mfZ)x6x^q?Gw@o&9Qkk7Dq$6yw>pf1R=8QeZtMZtG>YTIl32=p?9(`gO`3*$)E0 zzkvYc|6eq2Ql=+*{P{-uhow+IG}d)6wsNGS{a5{e>HB{$XaBdZSH{c84bVde--v&N zjQ(BPiiH=HaupEoAW-u5mDq%9iYg$++Ug<0hF8K2021@*^8Og#*y4^lA0>F+XReGu zMCKxDcC89dd9!y0qa<}m7PYV38$@tfdRlr;6O(i&cj=0utY|IGlN#G05?#0xu7jVU zRmFrrD#8v%oHuYDMt$im9w<#$jG-knO-p=&oyI)%zfv(S!8qNh6y%szAt$_`*0NHbRp7SQ^;cTK>z= z6{=X<7O=yAdCz}>EuX7Gk_M8N8VyO!DzCUyG;eH6#1VpJ>Y>nEN((iue|sAu&Ko&$ z9<~2AsO8~)n4DxHP086HT+t~j#h~K}oRKMeqY9*1H&0?nCUe>`puZu(lG%8;X$uItV~+9N}Px z8DjI~3-NSUJ-uUH6&8hgiIw;P!fOqd{?C6{{G`s0h3CGr^!`B_;b+5YmGf`&r1r0irNV{Yn-tjPO= z-9v)-)t78|clk#i^mR%HMx5*bjX(^e$>z#=jm%%oLOS`J2fm3MN>8yvjtFgh{ptfB zkzqGCYOZSXe~yI-;#qGeu``o&ekEO1ecqLl zNUJWPxTLk~Xxse6ZTnEqo--oNA}vg;9*Ze{$W8DnGz08Et(LCv6fTh9TAwrh!E~AZ z>29K#GK|Ygel<(yOX4c1m_l36z*<$e4}oK^J#h_5Gk{;xXNQ~`z(7tpTxWAoR|e*1 zcrieZ3r%N-EE`f6pY+GNtx8bK`<59KV{4&t!lR0_HKW5KvZbP9=*UDrqbY#a>4pep z{Q2yg(bIRuK~|1AI!Bfn+SV4=R<^M#n|C_rKr!kw1OHyGAw^|MI*dG`P8 z(wQhAHAoL1bmRY>cWASqqAk+A6+n2Yta^(%Z;sV0A!(v=)x(DD(hyXn(1gpz+SJ@JJ^lXO1qf&s||8{Lf>GNly^xyH8jAgI6fCvDf zt_uKw{sW}{@DPq>#>P&LbpKo!{^cq%6E$r&@R3J$$ga5Xw+}RFtt;7;mr5;)%F2_q zvmtn+fA_larxz}Czr(V(P|PEYZ&pK9r1!W+YYp!WIxTgwo(zIkvZQhY4R3Z<*bdEQ&IK)tzE& zuKJ|{8W(S$OkAMGFgF_=n_-FGUJe|Rc5Dd}b%75`?0DK!C1MH3JXF(1VWM);Jkn%= zPzSHjs_m%hqJcIOf3rxR&XtHGD(DFCxqig;5Q>~m=LT38FIv!_M zBB0!=mth-5i z8?(00iZr?Bqt9{%9x z2VVbFiJ4o`o2>{zxS|zr{Q?Q6rK1tLl zXrqU|s}(;xxItL5_4#;yy1Ietz%ydN_aY;Bx(YKGMVY{YCV369wg3^L5dG zLTBs!xwEi>r`!GYHPX>>F3;xr*S3fFZYSZ-I1-ZgV1>`Ns;%y~`}gxxO2xh(O4dre zSTcK+n*qq6+f2B|$V+c{E(#G9W)*vV{!(~?eTT-~TBCwy=g?asr^qvi7Qw=?^yF~A(WB$x2vy@n7fj{ zUo~GRy1aubPu7sj{7k&%`JT?czGT%^0hnG|#5FbA2N-mj%2^EGAV)caARvv~Ak1ZK zzL7UJ;aQsAeA641G0KC$M|jfFaX%Sf~A;L`jXupRZ#D~;K{4%Rq}rw4>%{CEB8 zs0uB!#kAz##I)qO_UPLTq-0H?=lYGF^u+X9ytMIw{i?X}Ig#MA+dC8Bt6awE6?uOQ zCA#{dktWfN{LeJOT@x{eZztrgpX9FTX7)~UQm|#5(bLP%$~oP1Sy&>8OGtYC?}>EW zl38qEZRCAE-+TIaGMBbmhVCgNI?dgGRg)Uq?*LA!KYIny_HC`C6;Xgt!8y_kg>BM`?c1 zaaUw;IVHV8Z6Yqhw9toY(SMNH3p53~s7i^SMIamtfJrI01l#I7 zU3uIXK}{P$EM@xj!6Uc5h*k$N4aCwHPqTplv)PgG^l3f1#LM$i#jgMj+iD6mS{-4Q z)OyG32lG-WM;$N0|9cm;`pj65573%fV97Gjev?p`syI z>7!K@Zs!gm=CepGn`02fE)z;LVoTfNDG}0=kHr5OVv%m$U$z@wS_&}%w;y$=E_*K) z)MET|dLRvzp;_=8Y8hvw<+c}zMtdi?E%kQKj4Y*k<E`@4i&|(CCjpTUqDA zLdh^qY9>+mF3aZd{T#lijHHN`^e_$jfUP`A3Qyus5PH-bsQ@liQ`?c2l?Ak z4~7j)D8M_2i5tfPcD}yv$r_d(=BTk3WlU-(Tf3C`yr$A#IeYGP6Wh}qR>te}fS#{W z2rI=!IY1@JS@%@gSjHUoUe?gjOz)n3g8I+p_K6tB02$$6rT$&r@=xFGLKf38xf-})z;s>SjJ7mebqil2(Khl<|c+m=Y zD~8C->U{14^!*5lSf?2X!_zsRN`QLbH9jl1X%yU;LwDqr$P5dZ#7jo0yEupVcr7ixMl zs7mQ?CsB;)D*p^wf=)hb~~c2W{gQ&hqf+OWA9cu9RMbfzeD zKZPN@Y?|3Fal-f%t@0n;mSDwW4DB5I=N3Cm8kb3=Qhh2{ zs{St;le@cpQM~M}lu|m9Ucs+E49wP5~=?zpG?kCzH>13oGet0B2Y~@ts?wakFZNny&2 z3Pk%rGDV`jc3k0Elk$+Iox0%(19O*TBkP$HgR_jajpnjiS=ULIWOhq)MCWOGLzVSQ z-TkgW5EPqFRwW7IRKe<3xau?qtc_#WeK9N9qC~YTuQ_wP;dVe?U(k`^a;24H6}l6n zWR|Lbf?YX4$PKz^TxV{3cC>~kMT}*0!8j5THBvcUcVbPj5Kg)c_|q=d3{dUUhn@FS zdcWefx$CU$K}EFgh+^mu+}>=@Fy0a~9qO8~fI8$h(bWc`1lr~5cJ>X{uoy}iM$y^l zjsX$^mi9E=;yRgw*ZL&M{7q&G`k2ruXHFXej)DS|n?lNMLyE0CFV26>sANUH?*lnc zcqI+v-!P{owjbpJ&AX4}eb?@O(Pm-2SDYqBMO%CryHSV<*_z1IQj_Ya(5dU_ ztnX-6Z-6=S?HfQwBn9OL%3`q$=>82mH8ajAw7h;zxNPM5x^^u|6%@wO6jM;%FpwZa zcF`P0iJ?hpcG+)aXd8W^Yg(9NcQduDKm5prs6bSdM7#jqdni zK=n_yOqPQZ*kJ=1wo#az}nYsNK%)$LiH-UtcbbllVb_vUr(m37WKH zDlg-J&Kj1vHwNDZ6EIF0GCGDXEQ_TS%YFP3v}q+@1O&*$QRs#2{?%D?hqE_U&gd*q zpYKpT4>IN{nLZCPQLv~DKUK0(tXN;P`-vA*6dcSiDL1MChrs||80jZ#SY7(bs!B|E zk+@Ldp!H6gGcff(UYnTX+?85$c3o()8($-}%VMM-q!GlxAG4al6 zax$p7$<%oGNVp%fGIU`X;#wU1m}>gdC3{fi|E{aCmknXwbJ+93b0yLZUdaPU4r%jTH*6l|o*7iF+x z&hpyo7_#p@>P%&OKv7hM>7 zbDd;HkY6{LS`1_pga-$c`|}7PN1+SfCG#XuasIZ_csYj-8AY_nWA9*bcCpOlTV!j+ zR`%<@fxQNO__1JqOT0pQLfNHg_PEjZiQ@k%WMFziV=WHIvyTY z;;Q+?K;S@PTAZDg){ZS44(4kN?#Yy#BW)qxbP({lTu=ZTrZUBGk`LQ9V_60R7ubt| z2?(YGX*Q9i(^uK+R0byo~GbH=Vj-jIluhTzJvvPcfq7OaznS@5{kopR6t3H zdpZmfVGjii>6PRNE1CQ3PSLcN08cI?l-nysATJ6HNMZ<2epD359=mm^FVZESQI+H* zx_j<8Libg{`j}Lqu`=5+YUs_V2p6G^sq>LlDJB*`;!eY%ib1_Hne->8jd83cnK8Ic zki1WeAW;-rkiu|QuWT|&-WEDpq$@YGE4fj%!)9)4SdRj#QqvH|ViPfs_<@Aa?mXaq zGBI5y+Lz3Y8YU7kM%{61K_HTh2oxtumErcF435Luyt81I&dCI*NCz63+o((u{AKKB zteiLxzd{%!iY~hSg{AF9lcB4hH^V6Y0(Lpi!`yt9bSxbuV=z>JuOiPt4{QNsmMoX> zb>k5F>-qJ4MfXO>>-+6$YYV-c%~0I>Gv_YE@A^1L0iq!)UT096hZZ*D4=PtVA=V0B zCw8{JCZ=zjAPoN=H{AX9-hd!{c@`#^f4{m zC%mgOVQ5O%cTw=b>}+H)YYbb(=qPrLyWNr`2R_UWV0 zLg2~9Gn|0*U8jrwigIf2Rtdc>pg>H_z|dAmn1}43Kpn;zhvBj`{(cZSM%MlLy1%3t zpy<(6Xv}9_D)zIT!Fujc}uRW^cE@Sn&nj`uGQ@0hK9KVPz~gSAJM~Y z(TdZW8|%)e!xg!wjOoMC3Z1q)IIniN19H?NbyDTt@-NZ64Rrfj`73h*wlk8W8=g~g z(K%E1p~T=GFK4)RVkfqXoHj(EJs$cqC7?>uu*s4wkQM5BQ%l6#)6=UP)$m&0L z#G8tJHXHQNpC|%C^}Bw_Nv zf~Ri z$Dw^6l6qG{qJqQG3;>2lnLEmcO$ypQkVB8&aPk0UpP7;;J`6c#Ud1!%V~=5UB2ZW~ zcuQ#LqJ6uvGS&|gn*hp?7x z<}4gX#?vDsZ{RCy4yZp+^Krk=P+ax9v^KyG=s?m?2iD( z_x~w`G5%W!v$oBkhacIITX*I6#)Bq~1r1VCtguLW%U@U2-KO*+KB7nSW1#Q1A%b(lC(tp zy*cKxUTy}p`-O<98N1%bTC#YLiQJH9lh+p~i8X}XosQD@MV5+h zTe)*UE@_T0_sqZ_I5T1lTRS?m1UM$*bNlGzowrRtAMOhbMeHp0-Mwe!ZZ<2)6p1=5 zh>J#7_{>5YJ0MZMF3KtA@tP=9`RYizA6a!N#aIXTy3c_dj+_hIHDa(yn7Y{y(XEzD zZN_S6Z7VE2@XjfDOFFKf4Yla+UT0(<0~bgB6;6R2s_|IQRxue{BUuYEOo>GD*9?LW zn|8k0yW82$!JO=|?LUC}r^?jau)%Ia_(aj)1@q8vaqB^^_cb%&&RVTw&0x=H>8&kl z@(W_CjhZ^%xlhKEs>|TfPU$Xf!3+Fdmwt$iT5*odp{mQbz2(K#`Tl(Uc0NJ(-TiWJ zP~wWF`~7tNp!)K-xw#Do6AheGpsIOrV+qT^sty>~Qt^WHQ>t3sRzx+>wl=dal z373EkC8VxtNFHgA9ZS>Gvu>u+n49iRuT^c&who`Vjf@iyRb*a2i0f*+3sbh^u_GAt zJ}(BYTFC@QJIV^|dRJD42lm-A;wrCUIHqEqjmY6a5Heb`INw}|46}h~%5_JxeO7)Z z@~&Hy=dNb{1~7P539$6%Y4wWCsbw-0?hO~G!9t;4B2rL|nkfDQerWrqO_q}jl+u2HDN@gp1c_p@336!Cyn*ep&5zK*tj z(GkGHEt9ZfpcU+`73IRx_6Hv@fWjKGM4W03`{{`&F+Tm_j-lRB5eKuKrU21MP*vki z(@Jg~tw@5B|0qB=iyf4q&>Q%>tEU_ASrY=YmK`SWX#Z-gbymxji>*bd$ca_Vj3}MR zE-kK0N5kcII#4Dv;6A+B-9YzOcX^F~P2|KbQ$fo(*g@(7&#eHE^E~!Lc{D#-{DQi_ zp6RP7mvr~v^zyOs_*MEhy6`^|TWqt9ybC$gbA1(&!*F&P{M@C!9jdwknp9Xy;>IT> zrJ1{qRy@m=;lPIR$rCFuy3=3RuwH13!2)vcv8L6m{~| zN6MpR9HSRctt*QabDZNNjospRjhkEF*Ha*H9}&3-qW}VCw_86$p z)MZofmHj=L0{#XOhCS;5laI&NGxj5at0<9i?h$hgsZ%^GZF-aX#;WuJDI9||tfn;4 zJ`EjFYk43g2KZKGMu7H1)PUFj&bw_GERS|p>XZYSz;FV$L|#_Yr6PF$ddung^!#pn z9l6-pP_^yxfxi1V8!8wedcgitgUPWSKgqa$xL(>SnL*?Gyy@Zdd4F8kN{RTS!@G8o zqsz|nI*+ILjTfQ5$jmS#q9Lu8I=5*yY+_JW5CvBgqx8hW2WZcDhFtZ;%0)KHi^Xgy z85)F#fL-q5`zgNUXXXZL?}w7UvDXxTR86X2u;Z- zwUv@CvSvmVJ%S1en8)(x_LG3&5w0o4`$i}j(!+l4460a#t2aW~*^U=cEqoQWX30|+ z>-H_y(dkGhK>^86FB`_07NgI~4##@319&|$Q9vg`e^&(i)&{O0*P=C6&-c?*Jl95xl?#TG9r1pxC175O*mLoi7IB>xnR6ki|9)nw;C@D$dN7aPk08&K zw1z45x{40X#KWNN6am3V-w4n%#FePVLp8}jDRv#?2B?yVZgE*>+VW&{d(7bKurHB2 ztsD^%(*3F<8cM8_5m~kpC$Nteo!9D$c~JwOD!3QXD>Ak`+mWq(iC$StmunJvXniiB z9NM_4D6uosiVvTW55ra6ch&$!@bB$UNCNs z|M8N@P*%n_^R?5_P%E;jjhdI9_tB1HrrqB*SX7x zsnx2`m8oz}Haf&32u2l-^q}Y@%1T||ptRnATE;Z72O{s$FC_*6nDx?W4nh7SX@+>ICL>f%`3C?P(5>A^oh+?w!C&OK1<(tL%7PA(2qw zEwog8O*iC*lX0Td-W0j85nb8rzmPa-lI1^G@5mYkbHok%=wgW#4lUsH6n{Mz2OU&r z?$&alk&|7<%r`@mNkUDyR5lF0*atf!{`)Nk!iK?6aiZN5v#i9FzVf&H&md6`@(8Tb zPyVA`Vl1-T0%7Rf;t=Hz(wJRgXF|7chb+_!_>W7{?M3sMJJNZz+X4=#m<(kv1il@& ztX(H4MA=-KSbNz^U0!M$9TV@R{Bojqr1*r`Mm52^4dEKABQdYolV5?&fc{5jiwuZci~=O97#NrFisyr(u)1)S|2U4?`?XyumVZ{%chHo5K#(CJohs?S(KWlDJ)h z6*g2?+l=A(wkx{$k*WVqN*6?}j*JMHb{diqll|)rgT^pvwMjH!(aUwLkW+!fU*z!a zZt7Ljxq^ig&b$U4NWS*Xw+^QV(0dZ%8q|5d)&>BmWUqkqF6c#520v?os@2pw4G}{? zrC1hv`UxrirW7V$JA@c+BmhWA`bR&2Lr$9IJOG<~2>Dn%xO5O-W?i}3u4Gwx3tdY9 zL9`VxfkvG*DusS+3gc1BE`j)d9Et_{ z|GBU_dg;aJ#jTIpqw5jWYWV!c2;v&1hR?U`zK<clf(ARKryMmlO;aOe&yst$!XIG9)lnFDEd4OBjmNIuO;aZ``m0L8M9~J5|+r z1Zv72r*0LRKp-$ya$&iPB0?lgEuElb(HM+vBno7{b~x-@>4d-suHn9Lgm8NJ%?!{t}Ud+n^1}&)jxs-Y7WTjhWEpd~q+RZmCv6p-IBT8_j|#Lr9tRYQdhL#54pG6En0W z3IjgTP|E74k^sV7SC*V(&a}^rEYzDEwyjUX%FLcIAej0qi6Olu0+EyQ+6D8xofdUs z=v=3IrGl_40qtRlv&TqmX9q>wv`n(*t1p)_>+E*U&a@-|vla#BP6#&KdSz!?@H!vW z9g|EwduT=3cRda~>N}MuoyURxBv*s54Fp%V)j7h!X&gh&)N+UCjD%cnC4h0Sf|=bw z-;iVyvkQ?A?~ia9@Bg>~)^MjP7XFDkf<^dOT>L+gAWmk+*2Z-I)c*v59I8p!Zm=VF z!_9v4pKfbx8%=~N7B)q3Oe!$rJW5L~&dVqA&pUtIoZS7!GHgqu20|`%j4Qq2@%H3$ zeERC%J>*KDq#Iht>UrSl7gic>6JOcp({-^;!Tp^co_eoP+vI96?&0%!+(Okpk3oh0 z%RNuU>B?9`wPH?Oq*i*Vquv0po1}=kG*~q)+CU;Ew`I2=>;|mB|G1yXA2s*+hIowV zSewnF1=|U#YEp3UsOtClblu(7D*mE`*I~S8iRV=xtiAh3|8=2mg%i6OE!y2a>urW89>&dBJofarddk`_nw--Dvs9(?O;w>!5+^lHI8X-kcH?k$j zr^+}E0CcQk7F50*ij*1formI?LYz!BrL3F@R=YvtxAt-3#*;Wq6%dkX03lk;)iYXB zJvXmwgDjubRu)#3ycV-yyrXx8T;^xqENuCqduM=%6TFFe2C_0}Ow$;S7Nt2=Lb--w zxwtZ=xH2`Mn#d}Uve}E!ohinK5?+jqh9(esd55#lc6C5TqX<~UAB5?R@)7@7gEVt+ z^tF=MQ|sgSRM=QG=7^Zs-h?j52s_`Q`BGc%1@+E zF%`v;G&;LWl8w9W0WUM`rU9;z;NbP4h17x7(KongXYt4U36i;p_Q>H%*kSL8*WaEz zOHKMG0wLK9B1j)NQA79Nc2yJzaEv(--Z;Dd*!rHS{L${j`Ah3kxx)R1zXdAE z>CiEe=qKFz>tPGxfeUyZu7lf4(W352?BN}oLjeqkiV!o{Hg7otO5fS*yPS-seqPfy zZNsD?pjkzg%VeScoxVk0!)RfszYgaCHS%ZKp_gp}^{j6VwC!Lo+Z&nsqQ)t)*gXyP zE&^}pNdZUoeGwX)E?W>6Pg@%Oab22CiQRcM5@q1(WGOfsH_WEgPJDGc*sypd1fFEk56p3`}kE-#E6UI`~o)n@7qaC)G2 zYtI~Y!TT57@T*xh?H$UCwJ<3DgAcf~B}iDnPUikf?O2OZ6bQ=b(m!&I}e zKj$V|O;cSUuB_J$CBwx*E&(Nu8U5cN#6ccGT}J{Ub&Aw~4&MG9AM{^UH$TJ3c?}Q% zfL=TR0GNMII*v~6R>qG1lG-NKHEs9Vk-c@zzlD&^@q}oJ{qXga)(NASxiHJL&2?oA zdee!cu^0L)k9%(F90@kl0c6gkB5>YcxX^q?cyYbe#q#d`)g{fi6{e!|DA}LQ`S*v! zKTq7tpOi_C$YJHh4Y?KA*p+)CzdsC9?`1-BlF=POga^;WwH>mdop_#7*agi)NZEtw z9LEU<^TtsBq|-XPxLFb%az~ji%B}3#9?sF)aH&vFrwerIWUV~b=^zJGn=r@lrSNno zKVupb;)rP*^vJKPKh`&s+C8jzN0C4+8WnOzcLWkNYY2(K3qzsEE(`#k%)=0F$GT6u z$E@5J&r>Yr!F14j?3fKUZy2)m2uNrfLJpyQ!V0V54EPdQ+jnlRgZnbGWu> z%W5bbu{MLLl*L!B^2TPxRnNU<agT2Ul z00nJ{7*I9d3oJMx?vq+$bam<>A6cXqu7XSDt4jzBzI^j>qAoA|OKzHd_c|9r&>p}K8pAH7J4$#CkY5F_?9e&DBOhFJ=2L)w)Mlv2cy|@z zJ_q;YJ_3c4zbDbDEdlDYB(_};jRKGokQ0gbL^C0ZPAfs~TdKkeH?7~n3b5a`wppRm z$SNp}AeEi(Xca`&XJRas8G_oD#ubvvDaO~7pY+^By7HaT;zh9c_G_cUi|Y#`KL9&I z3+mu@>|NTmXXx&xaC7B)O7F+z?;}_4Aqf?Z{nkl0DbEcZ*>-tY3YGe)+Cf6EpeQaQ0k%{`?%k`-3DSSlbl@l5TX#uy8fP1W7YYqEj zq1m=j1rx(lT^n#!`7-+m(>2=MY@u4;qB-JCW9N!}^Pv`pYBw!~Q|xzml3zjxpNHXkhck~|dyI~B>(7x<2MRjoa#_=sg zqOz;Liq{CMc)Bshch5mjiE7M9d#GYkEUZGRGu~;Qzjj{>CFFq~lqg6agb6e47`dL5 zCXsZsj4KDtietX>fivRd>|LXkrQ;t;UZ3Y$6fkbd6%o?8Pxs6ziVMD?!p1}K4x4FE z7zk0iM}#HG@$qSpMp9Xqsgewe|5U3zX{o*w z=bvzB^US2(Tr}xY#GN(eh){DtEKGR+$&=m3&_c-SUJP_BPc{nI&+iEz_ed{uDU_#V z2zeCoD*wBsXx)Noz97KXrSKDla*dQMx1=wwM9iMikCF^rU5c1#M$m`Wz%I?pLV-z6 z6UK^~;$6KKc!Ty!m0sgqZhG_GQ(7c^^K{MiM2H|I+O2*$Lx zm}c)Mn5X4C9e9@MuRrBV>AU;u=EbOkyniXso=}xu&4QJLAd+N{(qP;dzB&owfWsz7 z8t5b$)H68GszCPr9*PLNy)Gzho4t#p3D$U_`;CE;Q{eL5(dFs}k{o`XJ`!EE!ndNSG7}H~bcLiUd#OgRtO*p|s$0=qLhrR!On~ zu|H?hWpwQ0vEAmz3e2k&#_(_%+mO!}{mZ~io6om5R0eWQ-)sIR5Ha5EF70}&SJWku zAZLI{2P}`jb}7K`JRry}jUD%$M}2}d>aczz4xffGd&C=5ym8777YlJX`_cujDRhs% z-8qA3$*;pg7n5*^*NjJ-6#$>c!cz-5Zgv<18lktaGgCDpX;u_>TY3RKFJfVTCyfV= z493EB(}${?5_ut9O^V`pa@V(w>iO8(j?@8bJ%JrEs#~oZGZF(^<_y5A#;vND1Yc>v zg(#vABn2~g3XJ3kqC0)(9Qg0IrS|InL`GZ5H52fY#{nKhsd2nLZER7c?lHX2+vleW zqE0LdSWqRbH%Rfy?LxReu5%cTaa;PB@YCj{dZH486HA2_BN4^JflP8#^{vge`3V(75^8G%hEvn-JX2()y zdUx<|SFfCG^;dIJZWK$O&p(@Zv<_m3$8mlg4L%6PxaRr&X?`dhKsZ zMSt+2q%N1K!~!^D(?^p)6A9PAs$mP{goW)r2Ry$2IzuB{09+5Go4@-={>H%+ciw;n z9)yEv5W78k$T0~;t=~>(<%SzbPkqglzl2&AXE&HBG}EmTlScX`6Bf(egAVIOi)(|gD$ zmaoLVKOyuKQ1>pMQ#PR##_6gs<(u8?-h z$y+q5ke=7S!Nl1N2?a=bRu*KN2=et+_2&Ze*4*gq^Zhg0=T$ktv@O{qdv-@@kKmk} zRUjFQML&aH>VZc+4*`1|_ZlZQ_`R;ltL>eJ4oCyX6Z0=@*}SWeC35Z)ZdG{jEs>K1 zG6Cd5lcieu(v*rMSyDnH0juJF9LhAj!afzbXy*PMd{TWGzI?A3P<7omH z;?O)I+wjO^USW#s9G?MB`lNXs0N$Mi;Y%O`w?_h>C?)GQF{N8d+k6Td51VD3SlQ)T zgv~NfGt-q9QZLk8GTegd2Or{g$x6?7ziysBlM2oR6#r16<;_ zg5CTt^jGW9gdK!e?&#?eoJ#F`f<{lNF7MvB74Xw&$mu=0&2yjs(P=2F&Wj8Gq?A1U ztlod}NB)&KVrXk)V{GVTZfo<8%UDd%w%njc4!!|FTX#a{(JjIypDux5XuE3woFC7E zRDtf(N;!&7WL;W%rUDsy$71GL@Ahh-BSDbM*epX-LEsd*GTf**GPi`uz67-9U&|Gx zl!>WXo>ZU-(zt91Fwhcx(@qqkNO~)vKVDH1YFEyxEk!w|X`wdOJgK!~raxG5){zt& zo~(J{T+=yM<<#R%Jgr_-m>h+n#vsNDQ?VN-M(2QhsfHJuq`BF_ZqO7pDO6u*e_u z8>U^M@(L*WPE4Kz$(c+gl*#ZfayyT=l^h-sDJ-2b#H5+*1}Z3e+Ql+t2s(us^6IK3 zl@q=-M@r**W#Z-F6b;usHBP220TtLbbwO~x)LI$j zeu12VwIl_bSc9UvI^%Nd6YIoZP+=IBdIuWv0sfJiZkPEK1Rr(?v4SAnCP%HCk=9QYXcpztfK_U(%H z!Hc3MNo``%N*c$!5k%dLLX0(8L^@nzo@5;RO^$<>C!s6T2tOb7asW+Z*cH3iae}gA z?OORZ{F?r`8RKxu&Y@La1XcP7@TG-mm{m}%5u9E9S}|} z*i~qF(Fl@W6CB$2lKh(91=!&9Y*#aCJRmQW4`lOr;lhT>0~cGvhPy2XMpP?1z8N^i)S~$1&v8G^EaB+ zD!Hb-?{QVeM4iqVG>Q|ypTjPwyqD^0)|eMzZY|T|GAG_B9DCh73(!6nuhDO2D-bL; z_r5$AdE;Q_a~>|U*~;bFK#X_HiX^+R7FA2CIX%;YY!f=w^QA#_Ht#G0P~ThAqyLz` zboG09JQ_w|M(-Zsv+MvLHPcJdz)0TQnFMXq{2p<2Ym-_7wWYgFJF1Dsj<$35EA$Ks zwlvELV(Zw%8I{f5rBV%uT;Y7g{e1(y7$daD(#cu)6Z)BY%!KsKH@A!;CuwJyJ@+X* zageB)3+nz9w5?=*l{tgo2qyv;V}6Fu&Nzx=nRE0W?7HwmB`3KL-lZF4(#8yVQ+WdB zOv_Salz5>fIV_0lTF^a_9e`W{|(==pOl|I>dEW z9txPj z=@+gbu1`BdpfT!!9Es|dvM1DPU4;=2*QIC9E9w6K)!ubRHMMSA51s=eDmHomQ4s+V z0qFr0R1}bactE;H2dR-x$gxnQMT#H-p^33UR7xm;2-4An9tkB<0|W>Vl8``>$36GH ztA~5ukN5xn+ZlVzImZ67_8M!fHP_rr{3eRET@%_N+H?sE;Dxd5t%Z^nlS-9qOm!{P zucR$23}8ghM60<1Xagf#^x+y@m;IWEt?%vZU{U3Meqt=-oUehOx#6&itU0sXqFpSE zRT%j2qx+nK%=HSH4AK@xK-)2{?b_*E0{MiQ1_d1U^<{QLNo&d6=hKk#OP#i_;Crxl zd$D_tIvA2p0?I9Msu+e?nncYOKsP+u|4i)fwk2^zFON#~eLh+B*~`SAPp(|!>n-kfOSBF#4uKZG!>m9J41qNVo0KEKq9Nm0LvmqdolNfvQjIM699>X{+{ z8FA&#^ZMZOUx@?VIt|Yr`m}o_r0Y8PDjg_lXwKlx8|sq=qY5;yln|?)K`Ik`)}rU; zL>p{N>FPcOvtW^|dvPj_50Bu1zQ>Mus%YL_i+*qjX&IatB{Dkpee-Yx_hNl}azE@n z+Nx3oB)0TjMn_g8UkDiv{zDb(rItJ+de2A2B+%ko&^wQs1z422Pfz*p-Y)5p{!DH@ z!g(XpsIL3!u-?+yi>(JSjjA_@MjqKk(P_GN7d*R@o_MIc7wA@G>P&)OH;_;t>%_p8 zvS}71r;9ZAJ`Q z7(5j0345!HOM02fy>V5_D`Doj?ys^Cxxk>4nHD7BvNx`k7xg?Xiur;-wf)aX@vcKw zF`z;#QR}xNqh9lk!^9^!3P&}Y*Q718E8unPNGO?^<+6{3ESkTiU#R#9(H{T2;@vttsGyIow* zsPif#i%3UxScMJ-He!ncj^oK)lU~5TfH5}Alp}F0;Zm0V>2#Cpf|KGg>uS|8 z%gx|tNt70lQZIlY3(Aw`OY8gn3M-47t71PeL5}=Z>a^rT-Di&J)(g;b(%M^&$>Si_rGZV9wblx0^^fW?QD8-M?7 zKcEP~UThN$nd>>L;pMr{WU*a`At57YnvB_=IQTW;B--4?>J~0CcyTWR9e%s3CCWHDIM#9(Io8AR=0m;e&9RpLoq6ugM^ME&!U8%Yz^FMy`*OzpyEF^HP}fR3i8j+aMOJH)@Z{n z*1&h}Exxl6@Y$Q@xEVOAp30w{jwz9WRwO&h@Ue2-7ccjb(fT9EHgtJ|Mrc_)N}=)l zme*)nH_GX59W+iI_$bcMsY-V(1m0^xd72xH7}GL*j?WF18+PobTtGNoR7nMO_VPn) z^rfqDlz!};mDW9HmvdTEX8bk5 zVBOI_D1_Ayr=Y%G59yc2y%6=JGSrl5K0(reG;GpRW(u1tTR-#L0oypc)sCrz8?un>2FEYfRkH7A#%N#gG6-# zGERS9?TroN5M;roylO*xa77OEp&liJ!v%!N5^@BgcFzY&qoJ+ z4N$qgRK?j~h(g(n_G!EpN0#miTtCSK!{&RhgKGVc#Xokg_!_~LuYb1HJq_~_X~9*N zz!+~SvBB2g;%9n5?EaXIybYvt--r{f6}=pJMRrcDF{;6F+lNE9M z>|$Yz2@1`~@;!8N>C4gd*9E|qDxwpz6@C8Tt1k{M5JK5u94$Yf7L$02*%<1zIXIOW zc>ky|ZNf%~`Z{LNmGdMhQNO_bK#?M|K>4ZB)Hya@@?P=bm~7E+?2_Q)jSWfBWwCk5 zYt>|J!~|oxM5MScm2qs#?hj*4#~G0b_@o58YvcmY5x6Vh#Nju3=0T;^)YmLwulkl( zkE?x*8&ThaNY|zs&Ffn1ofL_+1-6M=HM7zY|uyK6qI~mLTwFdF21$22d zs@rn+yCA*5_sgtw^n5(L*V+AenZePR4;wv<1a5p9(O>7XGJ~hxKO3>kNkpCkUNg{X zHENg_B74m^E9bp4(`Fk7bxk~xAk|NYy@HnO4=|@|Jg{7~jg#2fg^Ts@u|iwErXwjW8G$IUAH3Bc?2yd<0VcN86`W1cWvBWZ4NIAIPU2p%e5GOCI0&UGcz>ILLQu1h4(2 z*t^}-t~*ItB-~aJd!y+&u#z+8(`Zm?q}r==|(;c@WTjcIz3Xr`_|#Pc5HF{ zg7xvx;4#w!*b|heD~Oh)X()N)BwKu;ubu~q#oNQ^vNtodHfq1i^h6ujjI5|inElYZ}D_W1`u4Sm$=t_SQk$O$g3Z03A5$1V}qYG%6 zLs~Y`cG0IM`sJmBt<)MtaB7VsUX#x}z_m_un;uW}PGOc}NLDq^B4R!6ybNck=tWBB z0VhsQDtLm{yE|x0fy0kbdZz-~Hbq>JylI2^&YvW#^o`bx;Y1>SYEj-`aNn4J1Vt7< zOMB*tBlJ`-vED;TT0WoS}^dX6JO>VI>}!aaVP8$ zCouDr5o0eXs6kRpy>Ea7cbQ;oSd`M$U1L`I_ zlx_Z5`f7N2#K9_$7@MyBkZ_2VcChDF-Q}54h17D#(EMUYXnl|ao9myqcziLm;jy4_ zxa|igc_Lx;Ce<^=Nve75@bYWNVdJjD{_|Vk6fx7xaIhzI#oU6>UnMjvMsPn>H-o`v7N_wX<$J zPfr}>T<>Zh#6{^qmoWSRrdl3k^aMwJ%A0ED;JMsVVGvlAJweZ}dCsg}-qqhOLIk*6 z(Mky)kbLb89DVo}S_*tFhrrx^7vS_bCWrH$xgrnUa+NFuk4?BUk=oAo>?2G{`I)J6 zn6dU;5+VU4i#Cu(0#Fi5U5HSwAv}KbK6m>C5~0sYK|e(0Pp~(bd0W&YFD9kDuSSr) zxh8gk3;84_3*G)mQ;3?zG^X^XM)4ztN3xLy!c13rc(kZo2TtvregF~W3lW+Xq{5sf z_;T?d0a~T2f$D3j=?%Vt&cOMGjdUEe?cs6;AnGLSdfqo7bquZbtC~5pz5lkJeRNAc z=>v_`lIkq2p+B=(8`kf*RP_l`{k+EIY27F)=&D|>a!coEHu+X5nxpHZ0&_n$B$TX{ zXS^2vnGKw+4WVS5o1KUPE*aFa>+bT(`fIu;SH-0c}%*C@OAj6Qur*%mW- zLYaxn%gzhz(>+P2X?fQ-&cn;eVVG=f5d-XXVA$-f!$lR@kM+G?c@fiKD zfJp*!lgD6%gmMUD{$3l2L~o(ea=9ZR+0GSmxEzCk6-XUzfUPIEqz0!NZX zL04*0DkgyPfoTcMJcI}kgoue4&nAs=67o6l61mx_;rqW&9tF)gB!!9ty%F8i=0uFj zI?jr?1}~))f8nQq4#qmmh~O`gvX5oVs@FL6;_~_R`BuJFv}Wq^S2^S6w9 zxf}k>ezvo2c~+Ug810%=$gj`Bp6`|qro_v&MSdiP=c!c+2%_doJ*!fwVfSUf6<#-v z6gT}~oDXNaUs)2hSgxpG5~I*=`XPjC$dSg$=$iK8fa)sXUoyy$m6%u z7G4X%7vvMCUbm}Nz8GD;;Fm%?9-{7pLAYDG(!zNXf%ox{FV5kv8P6~00NHL&81xfX zJ!+PEYCfr`uPn5HX^eEenBqDXJmxWQI8NH`x9V&+@D&#M}t-iz)T$ zfe`Xle%pjTTCgEPB5+T8DAj^7(bIj}=n_)da^#GX*>jQcPc||p)x;247lg@^Ni_7- zU{27D$N6&`)|r=WVvMrhIi~aPPgcS5nLHG)dMeXyTYnGGV=s1uCDH0~&IEijY4B{H zk}K$@Qs;Q%2tPp`M`65`YSt_76%OFk-YNm|0`8Re+<+6%9p}!f^;Exvu3I;fM>yL@ z#r+TW+beFCMRwZ<2-`m5L)$iiZUL@;p4|2fF4+AaqmXS6-2b`*ZXeyFbZ6g(=Z>}# z`8E%vhoxNOT0F4(cBQ~tf4!QVw8xz-K$!UF}?KllKc0;o9d#7PrUFKN=HHx z>Da-2u8MSvV2Ox$`ee1LY#7HYv}SfPiL$F-3qExikUbhS%JVt$Sh2v3#9wTs&3b=% z@yX%>Kf%`Q_ycIe0Weay>H>?g}A z=-CteQqt@t$6jdJ@|7#x&#LPHy(eCm+~_H zbOMN@?4}=xTTU6hdphodwdD(?$hr5sf}%X#bPk;|jcXC@gnI^j6p@fDN$r>7UA(e} zfI^Ndg+zHd47r$TY#2J=g42pLi$2EKCuFa!F64F`zK{I$8!T4q&S4abDp!?Q)OuCe zs!dp5=#KHg*%xLW@%vwhb;Fmf^ieLMt`rg|8SYWB21<3?{Bm`fL6+hP4!12GnPkta%53o;17Eyd>LH#+T z^4_A}%hz@ifi=^+L-wC7N+&&tG@OgC<&ns+mQ>O?fAmh)qOugp!A;`oH&@}}hh9xb zrs#e~!HJH!rx)sLE}D8zakfr1zd9MVze_$Z=3L5>jh=yeVBqjKiGAnKI0R>90bWYe z_8w;oY_^=cAgG1QTK$8bCKu!ZC~1$M;iLDPJ)0b>?e3}k4m*?FTR}7EH?@z8Iu@PF zaJMWAJNRKzC}mSrVBxW`q+`Ix(fh~eH95)p|;XOyrwdKf?T0uT0aOwLTfxpYN z_YAZno45bJ)7!xOWht77cDoe<-es!7bZo<_ zPAr5t2HfNLU0}GpF$y>)&Eg$k=9sK;HTFLY+qk?ee6CCS5Mk`rX@}U?SK(6N6JTCnV@|;XZ~bg zzZ1CA#QO(yaa*+cFT>XVwe;Qz{;Qwx59qF4@V$S6{||TJojf~zVt+6l*w*d;U#_t` zS$3Li{9q9|_O~p5T5jxQ*}19ogGE&NPnMl~OFId6ZXNs}NKyNf;KvTaPUudp><{Q+ z?SDddDra{x>=aJ@U@+cRz5J()YA1fD2<8XA{^CFJKTBeEqW`Lj_<`NEYtCrb-)SUv z68yD%|C!*AZK1`F?D&to_@kKL$@16w^=Fn=yIs5fqnh0b|7*4JGu+zoC-}c=kDchh lru3iD%C~=hguhGh7N);#zjXhoaSrYh+1{2@zw?h@{{yElq00aO literal 0 HcmV?d00001 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 index b38a814..5b2e245 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_best_results.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_best_results.m @@ -86,8 +86,9 @@ cfg.filters = struct('is_mpi',0,'pam_level',4, ... 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(baudrate.* m, ... +netrates_vnle = tp.calculateNetRate(grossrates, ... 'NGMI', ngmi_pam4, ... 'BER', BER_VNLE); @@ -151,10 +152,26 @@ cfg.fec_lines = []; cfg.agg = 'max'; cfg.figure_number = 47; -% fig = figure(cfg.figure_number); 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 = {'-'}; diff --git a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_complete_results.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_complete_results.m deleted file mode 100644 index 5cd72da..0000000 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/compare_ber_complete_results.m +++ /dev/null @@ -1,170 +0,0 @@ -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_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 = '-'; - - -%% Grid configuration -rows = 3; % PAM 4,6,8 -cols = 4; % DSP schemes -pam = [4 6 8]; - -cfg.figure_number = 46; -fig = figure(cfg.figure_number); clf; - -% Create extremely compact tile layout -t = tiledlayout(rows, cols, ... - 'TileSpacing','compact', ... - 'Padding','compact'); - -% Common cfg -cfg.show_precoded = 1; -cfg.group_by = {'equalizer_structure','pre_emph'}; -cfg.x_axis = 'grossrate'; -cfg.y_axis = 'BER'; -cfg.y_scale = 'log'; -cfg.plot.custom_colors_scatter = []; -cfg.plot.use_cbrewer2 = false; -cfg.fec_lines = []; -% DSP scheme 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; - - -% === Main nested loop: rows = PAM format, columns = DSP scheme === -for r = 1:rows - M = pam(r); - - for c = 1:cols - ax = nexttile(t, (r-1)*cols + c); - cfg.ax = ax; - - % ---- pre-emph = 1 (solid) ---- - cfg.plot.custom_colors = DSP(c).lightcolor; - cfg.plot.custom_linetypes = {'-'}; - cfg.filters = struct('is_mpi',0,'pam_level',M, ... - 'equalizer_structure',DSP(c).eq, ... - 'pre_emph',1); - plot_measurements_gpt(dataTable, cfg); - - % ---- pre-emph = 0 (dashed) ---- - cfg.plot.custom_colors = DSP(c).color; - cfg.plot.custom_linetypes = {'-'}; - cfg.filters.pre_emph = 0; - plot_measurements_gpt(dataTable, cfg); - - if M == 4 - ylim([1e-5 0.2]); - else - ylim([6e-4 0.2]); - end - - % % FEC limits - yline([2.2e-4 4.85e-3 2e-2],'LineWidth',1.1,'Color',[0.2 0.2 0.2], ... - 'LineStyle',':','HandleVisibility','off'); - - % Axes prettification - beautifyBERplot; - - % ===== Remove redundant labels ===== - if c > 1 - ax.YLabel = []; - end - if r < rows - ax.XLabel = []; - end - - grid(ax,'on'); box(ax,'on'); - end -end - -%this is just a ranom size but fits and is fixed now! -pos = 1e3.*[0.0983 0.6110 1.4113 0.4733]; -set(fig, 'Position', pos) - - - - -%% === 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 index 29ec0c4..f90cf1a 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/generate_spectrum_plots.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/generate_spectrum_plots.m @@ -65,7 +65,7 @@ Scpe_sig.spectrum("fignum",201,"normalizeTo0dB",0,"displayname",'Rx'); -if 0 +if 1 %% show freuqncy response of filter measure = 1; @@ -74,7 +74,7 @@ if 0 % Digi_sig = freqresp.buildOFDM(); - Digi_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]); + % 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); @@ -97,4 +97,15 @@ if 0 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 index fc88501..a31fab0 100644 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt.m +++ b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_measurements_gpt.m @@ -1,111 +1,51 @@ -function h = plot_measurements_gpt(T, cfg) -% Versatile plotting from your DB table (with cbrewer2 'Paired' palette). +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_flex(dataTable, cfg) +% 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); -%% ---- 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) +% --- 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', ... % ColorBrewer 'Paired' - 'paired_dark_first' , true, ... % dark for lines, light for scatter + 'colormap' , 'Paired', ... + 'paired_dark_first' , true, ... '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 + 'fecLineWidth' , 2.2, ... 'fecColor' , [0.25 0.25 0.25], ... 'capSize' , 6, ... - 'lineStyle_default' , '-', ... - 'use_pre_emph_styling' , true ... + 'lineStyle_default' , '-', ... + 'custom_colors' , [], ... + 'custom_colors_scatter' , [] ... ); +if ~isfield(cfg,'plot') || isempty(cfg.plot) + cfg.plot = struct; +end 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) ==== +%% ---- Colors ---- [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 - +%% ---- Axes / Figure handling ---- if isfield(cfg,'ax') && ~isempty(cfg.ax) && isgraphics(cfg.ax,'axes') - ax = cfg.ax; % use caller-provided axes + ax = cfg.ax; set(gcf,'CurrentAxes',ax); else if isfield(cfg,'figure_number') && ~isempty(cfg.figure_number) @@ -113,143 +53,97 @@ else else figure; end - ax = gca; % active axes + 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 - idx = (G==gi); - Ti = T(idx,:); - xi = x_raw(idx); - yi = y_raw(idx); + g = M.group{gi}; + xu = g.x; + yu = g.y; + ylo = g.y_lo; + yhi = g.y_hi; - % 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) --- + % --- Linestyle selection --- 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, ... + 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 (IQR) in line color + % Spread if any(isfinite(ylo)) && any(isfinite(yhi)) - h.err(gi) = errorbar(xu, yu, ylo, yhi, 'LineStyle','none', ... + 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 kept scatter (light) + % Raw scatter 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 + % reuse stored raw data (no extra filtering) + xi = g.x_raw; + yi = g.y_raw; colS = cols_scatter(gi,:); - scatter(xi(keep_all), yi(keep_all), cfg.plot.scatterSize, colS, 'filled', ... - 'MarkerFaceAlpha', cfg.plot.scatterAlpha, 'MarkerEdgeAlpha', cfg.plot.scatterAlpha, ... + scatter(ax, xi, yi, 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, ... + % 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(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; +%% ---- 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); -xticks(floor(xu)); -xlim([min(xu), max(xu)]) +% 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(cfg.y_axis,"BER",'IgnoreCase',true) +if startsWith(M.y_axis,"BER",'IgnoreCase',true) for v = cfg.fec_lines - yline(v, '--', 'Color', cfg.plot.fecColor, ... + yline(ax, 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]); + 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 ===== @@ -263,120 +157,14 @@ for i = 1:numel(fn) 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 + cfg.(f) = filldefaults(cfg.(f), defs.(f)); 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 ------------------------------- +% 1) User-provided custom colors if isfield(plotcfg,'custom_colors') && ~isempty(plotcfg.custom_colors) C = plotcfg.custom_colors; if size(C,1) < nG @@ -384,7 +172,6 @@ if isfield(plotcfg,'custom_colors') && ~isempty(plotcfg.custom_colors) 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 @@ -392,7 +179,6 @@ if isfield(plotcfg,'custom_colors') && ~isempty(plotcfg.custom_colors) 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); @@ -401,7 +187,7 @@ if isfield(plotcfg,'custom_colors') && ~isempty(plotcfg.custom_colors) return; end -% --- 2) Standard behavior (using cbrewer2 or fallback) ------------ +% 2) Standard behavior useBrewer = plotcfg.use_cbrewer2 && exist('cbrewer2','file')==2; if useBrewer N = max(2*nG, 12); 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/plot_rates.m b/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_rates.m deleted file mode 100644 index 29b09b5..0000000 --- a/projects/HighSpeedExperiment_2024/Auswertung_JLT/final/plot_rates.m +++ /dev/null @@ -1,34 +0,0 @@ - -path = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\HighSpeedExperiment_2024\Auswertung_JLT\final\rates.csv"; - -%% === Read CSV into a table === -T = readtable(path, 'Delimiter', ';', 'DecimalSeparator', ','); - -%% === Column definitions === -x = T.X_Werte; -pam_cols = {'PAM_2','PAM_4','PAM_6','PAM_8','PAM_12'}; -titles = {'PAM-2','PAM-4','PAM-6','PAM-8','PAM-12'}; - -% === Create subplots === -figure;hold on -c = linspecer(6); -for k = 1:numel(pam_cols) - - y = T.(pam_cols{k}); - valid = ~isnan(y); - - - % Scatter plot - s = scatter(x(valid), y(valid), 25, 'filled','MarkerEdgeColor',c(k,:),'MarkerFaceColor',c(k,:),'DisplayName',titles{k}); - - % Fit (2nd-order polynomial) - p = polyfit(x(valid), y(valid), 2); - xfit = linspace(min(x(valid)), max(x(valid)), 200); - yfit = polyval(p, xfit); - - % Plot fit curve - plot(xfit, yfit, 'LineWidth', 1,'linestyle',':','Color',c(k,:),'HandleVisibility','off'); - - xlabel('baud rate [GBd]'); - ylabel('net rate [Gb/s]'); -end \ No newline at end of file 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 b5fdd4a..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 = 0; -dsp_options.max_occurences = 5; +dsp_options.append_to_db = 1; +dsp_options.max_occurences = 1; experiment = "highspeed_2024"; dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files' @@ -40,25 +40,25 @@ end fp = QueryFilter(); % fp.where('Runs', 'run_id','EQUALS', 2776); -M = 4; +M = 6; fp.where('Runs', 'pam_level','EQUALS', M); -fp.where('Runs', 'bitrate','EQUALS', 390e9);%360,390 +% fp.where('Runs', 'bitrate','EQUALS', 390e9);%360,390 % fp.where('Runs', 'symbolrate','EQUALS', 195e9); -fp.where('Runs', 'fiber_length','EQUALS', 2); -% fp.where('Runs', 'is_mpi','EQUALS', 0); +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','EQUALS', 1310); +% 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','GREATER_THAN', 7); +% 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"); @@ -70,10 +70,7 @@ wh.addStorage("mlmlse_package"); %% === RUN IT === -[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true); - - - +[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); diff --git a/projects/HighSpeedExperiment_2024/a_minimal_example.m b/projects/HighSpeedExperiment_2024/a_minimal_example.m new file mode 100644 index 0000000..de85571 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/a_minimal_example.m @@ -0,0 +1,118 @@ + + +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 db72ac3..7fe1e7d 100644 --- a/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m +++ b/projects/HighSpeedExperiment_2024/auswertung/only_show_precomp.m @@ -8,84 +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",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"); - -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); - -ylim([-30,3]); -xlim([-5,100]); \ No newline at end of file +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 43e5b7e..c042156 100644 --- a/projects/HighSpeedExperiment_2024/bias_evaluation.m +++ b/projects/HighSpeedExperiment_2024/bias_evaluation.m @@ -30,7 +30,7 @@ for l = 1:numel(lambda_vals) 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)); diff --git a/projects/IMDD_base_system/simulation_bwl_2.m b/projects/IMDD_base_system/simulation_bwl_2.m index 27a71cb..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,14 +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; -mlse_sig_sd = mlse_.process(mlse_sig_sd,Symbols); -mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(mlse_sig_sd); +elseif duob_mode == db_mode.db_precoded + + %% + [EQ_sig, Noi] = eq_.process(Scpe_cell{1},Duobinary().encode(Symbols)); + + 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/ML_based_MLSE/experimental_data.m b/projects/ML_based_MLSE/experimental_data.m index 6543e9f..a594a92 100644 --- a/projects/ML_based_MLSE/experimental_data.m +++ b/projects/ML_based_MLSE/experimental_data.m @@ -35,8 +35,9 @@ mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M 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 0 +if 1 [mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,Symbols); else % Ml MLSE diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index c72a985..0d7cbe2 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -142,7 +142,7 @@ for realiz = 1:s.num_realiz 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); PD_cell = {}; - parfor l = 1:N + for l = 1:N %%%%%% PD Square Law %%%%%% assert(fdac*kover==Opt_sig_wdm_demux{l}.fs,'Sampling Frequencies do not match! Check previous steps');