From a6cf742121a4ac7c3d2b8beae651873b5773338e Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Fri, 10 Jul 2026 09:14:34 +0200 Subject: [PATCH] MPI FFE with A1 and A2 algorithms --- Classes/04_DSP/Equalizer/FFE.m | 661 ++++++++++++++---- Functions/EQ_blocks/ffe.m | 27 +- Functions/EQ_recipes/dsp_recipe_minimal.m | 2 +- Functions/EQ_recipes/dsp_scope_signal.m | 6 +- Functions/EQ_recipes/mpi_recipe_dev.m | 136 ++-- Tests/04_DSP/Equalizer/FFE_test.m | 191 ++++- .../MPI_revisit/investigate_mpi_algorithms.m | 65 +- projects/Diss/MPI_revisit/workerError.mat | Bin 0 -> 1358 bytes projects/Diss/investigate_channel_memory.m | 2 +- projects/Diss/investigate_noise_enhancement.m | 2 +- workerError.mat | Bin 1346 -> 1255 bytes 11 files changed, 863 insertions(+), 229 deletions(-) create mode 100644 projects/Diss/MPI_revisit/workerError.mat diff --git a/Classes/04_DSP/Equalizer/FFE.m b/Classes/04_DSP/Equalizer/FFE.m index df8b347..e624137 100644 --- a/Classes/04_DSP/Equalizer/FFE.m +++ b/Classes/04_DSP/Equalizer/FFE.m @@ -12,7 +12,7 @@ classdef FFE < handle % eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr, ... % "mu_dd",1e-1,"mu_tr",0.4,"order",50, ... % "sps",2,"decide",0,"optmize_mus",1,"dd_mode",1, ... - % "adaption_technique","nlms","mu_dc",1.021e-05); + % "adaption_technique","nlms","dc_tracking_mu",1.021e-05); properties @@ -32,8 +32,24 @@ classdef FFE < handle mu_dd %weight update in dd mode epochs_dd dd_len_fraction - mu_dc - adaptive_dc_enabled + + % A1 moving-average input suppression + dc_avg_bufferlength_a1 + dc_smoothing_a1 + + % A2 level-dependent residual suppression + dc_level_avg_bufferlength_a2 + dc_smoothing_a2 + dc_level_weights_a2 + + % Adaptive DC-tracking loop + dc_tracking_mu + dc_tracking_adaptive_enabled + dc_tracking_power_exponent + dc_tracking_buffer_len + + % Delayed FFE tap update + ffe_update_buffer_len e_dc P % covariance matrix of rls @@ -51,23 +67,29 @@ classdef FFE < handle mu_optimization_len plot_mu_optimization = 0; mu_optimization_fignum = 3010; - optimize_dc_params = 0; - dc_optimization - dc_optimization_iter = 0; - dc_optimization_len - dc_optimization_max_evals - dc_optimization_delay_weight - dc_optimization_smoothing_len + optimize_dc_tracking_params = 0; + dc_tracking_optimization + dc_tracking_optimization_iter = 0; + dc_tracking_optimization_len + dc_tracking_optimization_max_evals + dc_tracking_optimization_delay_weight + dc_tracking_optimization_smoothing_len + optimize_a2_level_weights = 0; + a2_level_weight_optimization + a2_level_weight_optimization_iter = 0; + a2_level_weight_optimization_len + a2_level_weight_optimization_max_evals + a2_level_weight_max + a2_level_weight_initial_stats end properties (Access = private) - dc_alpha = 0.98 - dc_gamma = 1e-6 - dc_mu_min = 1e-6 - dc_mu_max = 3e-1 - dc_mu_eff_min = 0 - dc_mu_eff_max = inf - dc_power_exponent = 1 + dc_tracking_alpha = 0.98 + dc_tracking_gamma = 1e-6 + dc_tracking_mu_min = 1e-6 + dc_tracking_mu_max = 3e-1 + dc_tracking_mu_eff_min = 0 + dc_tracking_mu_eff_max = inf end methods @@ -86,8 +108,20 @@ classdef FFE < handle options.mu_dd = 1e-5; options.epochs_dd = 5; options.dd_len_fraction = 1; - options.mu_dc = 0; - options.adaptive_dc_enabled = false; + + options.dc_avg_bufferlength_a1 = 0; + options.dc_smoothing_a1 = 0; + + options.dc_level_avg_bufferlength_a2 = 0; + options.dc_smoothing_a2 = 0; + options.dc_level_weights_a2 = 0; + + options.dc_tracking_mu = 0; + options.dc_tracking_adaptive_enabled = false; + options.dc_tracking_power_exponent = 2; + options.dc_tracking_buffer_len = 1; + + options.ffe_update_buffer_len = 1; options.decide = false; @@ -96,11 +130,15 @@ classdef FFE < handle options.mu_optimization_len = 2^15; options.plot_mu_optimization = 0; options.mu_optimization_fignum = 3010; - options.optimize_dc_params = 0; - options.dc_optimization_len = 2^15; - options.dc_optimization_max_evals = 20; - options.dc_optimization_delay_weight = 1e-3; - options.dc_optimization_smoothing_len = 501; + options.optimize_dc_tracking_params = 0; + options.dc_tracking_optimization_len = 2^15; + options.dc_tracking_optimization_max_evals = 20; + options.dc_tracking_optimization_delay_weight = 1e-3; + options.dc_tracking_optimization_smoothing_len = 501; + options.optimize_a2_level_weights = 0; + options.a2_level_weight_optimization_len = 2^15; + options.a2_level_weight_optimization_max_evals = 30; + options.a2_level_weight_max = 1; end @@ -109,9 +147,34 @@ classdef FFE < handle obj.(fn{n}) = options.(fn{n}); end + assert(obj.dc_tracking_buffer_len >= 0); + assert(obj.ffe_update_buffer_len >= 0); + assert(obj.dc_avg_bufferlength_a1 >= 0); + assert(obj.dc_level_avg_bufferlength_a2 >= 0); + obj.e = zeros(obj.order,1); obj.e_dc = 0; obj.error = 0; + obj.a2_level_weight_initial_stats = struct(); + obj.dc_avg_bufferlength_a1 = floor(obj.dc_avg_bufferlength_a1); + obj.dc_smoothing_a1 = min(max(obj.dc_smoothing_a1,0),1); + obj.dc_level_avg_bufferlength_a2 = floor(obj.dc_level_avg_bufferlength_a2); + obj.dc_smoothing_a2 = min(max(obj.dc_smoothing_a2,0),1); + obj.dc_tracking_buffer_len = floor(obj.dc_tracking_buffer_len); + obj.ffe_update_buffer_len = floor(obj.ffe_update_buffer_len); + obj.a2_level_weight_max = max(obj.a2_level_weight_max,0); + + if obj.dc_avg_bufferlength_a1 > 1 && (obj.dc_tracking_mu ~= 0 || obj.optimize_dc_tracking_params) + warning("FFE:DCAvgWithDcTracking", ... + "A1 moving-average DC suppression and dc_tracking_mu/adaptive DC tracking are alternative DC suppression paths and will be combined."); + end + a2_requested = obj.dc_level_avg_bufferlength_a2 > 1 && ... + (any(obj.dc_level_weights_a2(:) ~= 0) || obj.optimize_a2_level_weights); + if a2_requested && ... + (obj.dc_tracking_mu ~= 0 || obj.optimize_dc_tracking_params || obj.dc_avg_bufferlength_a1 > 1) + warning("FFE:DCLevelAvgWithOtherDcSuppression", ... + "A2 level-dependent MPI suppression is enabled together with another DC/MPI suppression path; both corrections will be combined."); + end end @@ -135,8 +198,15 @@ classdef FFE < handle obj.P = (1/delta) * eye(obj.order); end - if obj.optimize_dc_params - obj.optimizeDcParams(X.signal,D.signal); + if obj.optimize_dc_tracking_params + obj.optimizeDcTrackingParams(X.signal,D.signal); + obj.e = zeros(obj.order,1); + obj.e_dc = 0; + obj.P = (1/delta) * eye(obj.order); + end + + if obj.optimize_a2_level_weights + obj.optimizeA2LevelWeights(X.signal,D.signal); obj.e = zeros(obj.order,1); obj.e_dc = 0; obj.P = (1/delta) * eye(obj.order); @@ -212,13 +282,56 @@ classdef FFE < handle P_err = 0; err_prev = 0; - dc_alpha = obj.dc_alpha; - dc_gamma = obj.dc_gamma; - dc_mu_min = obj.dc_mu_min; - dc_mu_max = obj.dc_mu_max; - dc_mu_eff_min = obj.dc_mu_eff_min; - dc_mu_eff_max = obj.dc_mu_eff_max; - dc_power_exponent = obj.dc_power_exponent; + dc_tracking_alpha = obj.dc_tracking_alpha; + dc_tracking_gamma = obj.dc_tracking_gamma; + dc_tracking_mu_min = obj.dc_tracking_mu_min; + dc_tracking_mu_max = obj.dc_tracking_mu_max; + dc_tracking_mu_eff_min = obj.dc_tracking_mu_eff_min; + dc_tracking_mu_eff_max = obj.dc_tracking_mu_eff_max; + dc_tracking_power_exponent = obj.dc_tracking_power_exponent; + dc_buffer_enabled = obj.dc_tracking_mu ~= 0 && obj.dc_tracking_buffer_len > 1; + if dc_buffer_enabled + e_dc_buffer = NaN(obj.dc_tracking_buffer_len,1); + end + + ffe_buffer_enabled = obj.ffe_update_buffer_len > 1 && obj.adaption_technique ~= adaption_method.rls; + if ffe_buffer_enabled + ffe_update_buffer = NaN(obj.order,obj.ffe_update_buffer_len); + end + + dc_avg_enabled = obj.dc_avg_bufferlength_a1 > 1; + if dc_avg_enabled + dc_avg_buffer_a1 = NaN(obj.dc_avg_bufferlength_a1,1); + dc_avg_est_a1 = 0; + dc_avg_update_blocklength_a1 = obj.dc_avg_bufferlength_a1; + dc_avg_update_blocklength_a1 = max(1,floor(dc_avg_update_blocklength_a1)); + dc_avg_input_offset_a1 = floor(obj.order/2); + % Hardware-like A1 is causal; dc_smoothing_a1 is reserved for offline variants. + end + + dc_level_enabled = obj.dc_level_avg_bufferlength_a2 > 1 && any(obj.dc_level_weights_a2(:) ~= 0); + if dc_level_enabled + if isempty(obj.constellation) + builtin("error","FFE:MissingConstellation", ... + "A2 level-dependent MPI suppression requires obj.constellation to be set."); + end + + n_levels = numel(obj.constellation); + if isscalar(obj.dc_level_weights_a2) + dc_level_weight_by_level = repmat(obj.dc_level_weights_a2,n_levels,1); + elseif numel(obj.dc_level_weights_a2) == n_levels + dc_level_weight_by_level = obj.dc_level_weights_a2(:); + else + builtin("error","FFE:InvalidDCLevelWeights", ... + "dc_level_weights_a2 must be scalar or have one entry per constellation level."); + end + dc_level_err_buffer = NaN(n_levels,obj.dc_level_avg_bufferlength_a2); + dc_level_mpi_est_by_level = zeros(n_levels,1); + dc_level_valid_count_by_level = zeros(n_levels,1); + dc_level_update_blocklength_a2 = obj.dc_level_avg_bufferlength_a2; + dc_level_update_blocklength_a2 = max(1,floor(dc_level_update_blocklength_a2)); + dc_level_window_future_fraction = obj.dc_smoothing_a2; %#ok % reserved for delayed/offline A2 variants + end debug_enabled = obj.save_debug; if debug_enabled @@ -228,8 +341,14 @@ classdef FFE < handle obj.debug_struct.main_cursor = NaN(1,n_symbols_debug); obj.debug_struct.mu_nlms = NaN(1,n_symbols_debug); obj.debug_struct.update_gradient = NaN(1,n_symbols_debug); - obj.debug_struct.mu_dc_eff = NaN(1,n_symbols_debug); - obj.debug_struct.e_dc_eff = NaN(1,n_symbols_debug); + obj.debug_struct.dc_tracking_mu_eff = NaN(1,n_symbols_debug); + obj.debug_struct.dc_tracking_est = NaN(1,n_symbols_debug); + obj.debug_struct.dc_avg_offset = NaN(1,n_symbols_debug); + obj.debug_struct.dc_level_mpi_est = NaN(1,n_symbols_debug); + obj.debug_struct.dc_level_weight = NaN(1,n_symbols_debug); + obj.debug_struct.dc_level_valid_count = NaN(1,n_symbols_debug); + obj.debug_struct.dc_level_symbol_idx = NaN(1,n_symbols_debug); + obj.debug_struct.dc_level_y_raw = NaN(1,n_symbols_debug); if training obj.debug_struct.error_tr = NaN(1,n_symbols_debug); @@ -246,14 +365,38 @@ classdef FFE < handle for sample = 1 : obj.sps : N symbol = symbol+1; - mu_dc_eff = 0; + dc_tracking_mu_eff = 0; + dc_avg_offset = 0; + dc_level_mpi_est = 0; + dc_level_weight = 0; + dc_level_valid_count = 0; + dc_level_symbol_idx = NaN; U = x(obj.order+sample-1:-1:sample); + if dc_avg_enabled + dc_avg_offset = dc_avg_est_a1; + U = U - dc_avg_offset; + end y(symbol,1) = obj.e_dc + (obj.e.*mask).' * U; % Calculating output of LMS __ * | + y_raw = y(symbol); + + if dc_avg_enabled + dc_avg_input_idx = dc_avg_input_offset_a1 + sample; + dc_avg_buffer_a1 = circshift(dc_avg_buffer_a1,1); + dc_avg_buffer_a1(1) = x(dc_avg_input_idx); + if mod(symbol,dc_avg_update_blocklength_a1) == 0 + dc_avg_est_a1 = mean(dc_avg_buffer_a1,"omitnan"); + end + end if training d_hat(symbol,1) = d(symbol); + if isempty(obj.constellation) + symbol_idx = NaN; + else + [~,symbol_idx] = min(abs(d_hat(symbol) - obj.constellation)); + end else if ~always_ideal_decision [~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point @@ -263,6 +406,35 @@ classdef FFE < handle end end + dc_level_symbol_idx = symbol_idx; + if dc_level_enabled + mpi_err = y_raw - d_hat(symbol); + dc_level_mpi_est = dc_level_mpi_est_by_level(symbol_idx); + dc_level_valid_count = dc_level_valid_count_by_level(symbol_idx); + dc_level_weight = dc_level_weight_by_level(symbol_idx) * ... + min(dc_level_valid_count / obj.dc_level_avg_bufferlength_a2,1); + y(symbol,1) = y_raw - dc_level_weight * dc_level_mpi_est; + + if training + d_hat(symbol,1) = d(symbol); + else + if ~always_ideal_decision + [~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); + d_hat(symbol,1) = obj.constellation(symbol_idx); + else + d_hat(symbol,1) = d(symbol); + end + end + + dc_level_err_buffer(dc_level_symbol_idx,:) = circshift(dc_level_err_buffer(dc_level_symbol_idx,:),1,2); + dc_level_err_buffer(dc_level_symbol_idx,1) = mpi_err; + if mod(symbol,dc_level_update_blocklength_a2) == 0 + dc_level_valid_count_by_level = sum(isfinite(dc_level_err_buffer),2); + dc_level_mpi_est_by_level = mean(dc_level_err_buffer,2,"omitnan"); + dc_level_mpi_est_by_level(dc_level_valid_count_by_level == 0) = 0; + end + end + % err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error err(symbol) = d_hat(symbol) - y(symbol); % Instantaneous error @@ -278,7 +450,6 @@ classdef FFE < handle weight = mu / normU; grad = err(symbol) * U; update = grad * weight; - obj.e = obj.e + update; case adaption_method.lms @@ -287,7 +458,6 @@ classdef FFE < handle weight = mu; grad = err(symbol) * U; update = grad * weight; - obj.e = obj.e + update; @@ -300,27 +470,47 @@ classdef FFE < handle % Gewichtsupdate: update = k * err(symbol); - obj.e = obj.e + update; % P-Matrix‐Update: - obj.P = (1/lambda) * (obj.P - k * (U.' * obj.P)); end - if obj.mu_dc ~= 0 - if obj.adaptive_dc_enabled - delta_mu = dc_gamma * err(symbol) * err_prev * (U.'*U); - obj.mu_dc = min(max(obj.mu_dc + delta_mu,dc_mu_min),dc_mu_max); + if ffe_buffer_enabled + ffe_update_buffer = circshift(ffe_update_buffer,1,2); + ffe_update_buffer(:,1) = update; + if mod(symbol,obj.ffe_update_buffer_len) == 0 + obj.e = obj.e + mean(ffe_update_buffer,2,"omitnan"); + end + else + obj.e = obj.e + update; + end + + if obj.adaption_technique == adaption_method.rls + obj.P = (1/lambda) * (obj.P - k * (U.' * obj.P)); + end + + if obj.dc_tracking_mu ~= 0 + if obj.dc_tracking_adaptive_enabled + delta_mu = dc_tracking_gamma * err(symbol) * err_prev * (U.'*U); + obj.dc_tracking_mu = min(max(obj.dc_tracking_mu + delta_mu,dc_tracking_mu_min),dc_tracking_mu_max); err_prev = err(symbol); - P_err = dc_alpha*P_err + (1-dc_alpha)*err(symbol)^2; - mu_dc_eff = obj.mu_dc / ((P_err + eps)^dc_power_exponent); + P_err = dc_tracking_alpha*P_err + (1-dc_tracking_alpha)*err(symbol)^2; + dc_tracking_mu_eff = obj.dc_tracking_mu / ((P_err + eps)^dc_tracking_power_exponent); else - mu_dc_eff = obj.mu_dc; + dc_tracking_mu_eff = obj.dc_tracking_mu; end - mu_dc_eff = min(max(mu_dc_eff,dc_mu_eff_min),dc_mu_eff_max); - obj.e_dc = obj.e_dc + mu_dc_eff * err(symbol); + dc_tracking_mu_eff = min(max(dc_tracking_mu_eff,dc_tracking_mu_eff_min),dc_tracking_mu_eff_max); + if dc_buffer_enabled + e_dc_buffer(1) = obj.e_dc + dc_tracking_mu_eff * err(symbol); + e_dc_buffer = circshift(e_dc_buffer,1); + if mod(symbol,obj.dc_tracking_buffer_len) == 0 + obj.e_dc = mean(e_dc_buffer,"omitnan"); + end + else + obj.e_dc = obj.e_dc + dc_tracking_mu_eff * err(symbol); + end end end @@ -336,8 +526,14 @@ classdef FFE < handle obj.debug_struct.main_cursor(1,symbol) = abs(obj.e(maincursor_pos)); obj.debug_struct.mu_nlms(1,symbol) = weight; obj.debug_struct.update_gradient(1,symbol) = grad.'*grad; - obj.debug_struct.mu_dc_eff(1,symbol) = mu_dc_eff; - obj.debug_struct.e_dc_eff(1,symbol) = obj.e_dc; + obj.debug_struct.dc_tracking_mu_eff(1,symbol) = dc_tracking_mu_eff; + obj.debug_struct.dc_tracking_est(1,symbol) = obj.e_dc; + obj.debug_struct.dc_avg_offset(1,symbol) = dc_avg_offset; + obj.debug_struct.dc_level_mpi_est(1,symbol) = dc_level_mpi_est; + obj.debug_struct.dc_level_weight(1,symbol) = dc_level_weight; + obj.debug_struct.dc_level_valid_count(1,symbol) = dc_level_valid_count; + obj.debug_struct.dc_level_symbol_idx(1,symbol) = dc_level_symbol_idx; + obj.debug_struct.dc_level_y_raw(1,symbol) = y_raw; if training obj.debug_struct.error_tr(1,symbol) = error_power; @@ -364,16 +560,16 @@ classdef FFE < handle case adaption_method.rls mu_range = [0.98, 0.99999]; end - mu_dc_range = [1e-5, 1e-1]; + dc_tracking_mu_range = [1e-5, 1e-1]; mu_tr_var = optimizableVariable("mu_tr",mu_range,"Transform","log"); vars = mu_tr_var; if obj.dd_mode vars = [vars, optimizableVariable("mu_dd",mu_range,"Transform","log")]; end - optimize_mu_dc = obj.mu_dc ~= 0; - if optimize_mu_dc - vars = [vars, optimizableVariable("mu_dc",mu_dc_range,"Transform","log")]; + optimize_dc_tracking_mu = obj.dc_tracking_mu ~= 0; + if optimize_dc_tracking_mu + vars = [vars, optimizableVariable("dc_tracking_mu",dc_tracking_mu_range,"Transform","log")]; end obj.mu_optimization_iter = 0; fprintf("FFE mu opt uses %d samples / %d symbols\n",N_opt,numel(d_opt)); @@ -387,18 +583,18 @@ classdef FFE < handle if obj.dd_mode obj.mu_dd = obj.mu_optimization.XAtMinObjective.mu_dd; end - if optimize_mu_dc - obj.mu_dc = obj.mu_optimization.XAtMinObjective.mu_dc; + if optimize_dc_tracking_mu + obj.dc_tracking_mu = obj.mu_optimization.XAtMinObjective.dc_tracking_mu; end - if obj.dd_mode && optimize_mu_dc - fprintf("\nFFE mu opt done: mu_tr=%9.3e, mu_dd=%9.3e, mu_dc=%9.3e, BER=%9.3e\n", ... - obj.mu_tr,obj.mu_dd,obj.mu_dc,obj.mu_optimization.MinObjective); + if obj.dd_mode && optimize_dc_tracking_mu + fprintf("\nFFE mu opt done: mu_tr=%9.3e, mu_dd=%9.3e, dc_tracking_mu=%9.3e, BER=%9.3e\n", ... + obj.mu_tr,obj.mu_dd,obj.dc_tracking_mu,obj.mu_optimization.MinObjective); elseif obj.dd_mode fprintf("\nFFE mu opt done: mu_tr=%9.3e, mu_dd=%9.3e, BER=%9.3e\n", ... obj.mu_tr,obj.mu_dd,obj.mu_optimization.MinObjective); - elseif optimize_mu_dc - fprintf("\nFFE mu opt done: mu_tr=%9.3e, mu_dc=%9.3e, BER=%9.3e\n", ... - obj.mu_tr,obj.mu_dc,obj.mu_optimization.MinObjective); + elseif optimize_dc_tracking_mu + fprintf("\nFFE mu opt done: mu_tr=%9.3e, dc_tracking_mu=%9.3e, BER=%9.3e\n", ... + obj.mu_tr,obj.dc_tracking_mu,obj.mu_optimization.MinObjective); else fprintf("\nFFE mu opt done: mu_tr=%9.3e, BER=%9.3e\n", ... obj.mu_tr,obj.mu_optimization.MinObjective); @@ -409,43 +605,99 @@ classdef FFE < handle end end - function optimizeDcParams(obj,x,d) - [x_opt,d_opt,N_opt] = obj.optimizationSignals(x,d,obj.dc_optimization_len); + function optimizeDcTrackingParams(obj,x,d) + [x_opt,d_opt,N_opt] = obj.optimizationSignals(x,d,obj.dc_tracking_optimization_len); - vars = optimizableVariable("mu_dc",[1e-5,1e-1],"Transform","log"); - if obj.adaptive_dc_enabled + vars = optimizableVariable("dc_tracking_mu",[1e-5,1e-1],"Transform","log"); + if obj.dc_tracking_adaptive_enabled vars = [vars, ... - optimizableVariable("dc_alpha",[0.85,0.995]), ... - optimizableVariable("dc_gamma",[1e-7,3e-5],"Transform","log"), ... - optimizableVariable("dc_power_exponent",[0,1]), ... - optimizableVariable("dc_mu_eff_max",[1e-3,3e-1],"Transform","log")]; + optimizableVariable("dc_tracking_alpha",[0.85,0.995]), ... + optimizableVariable("dc_tracking_gamma",[1e-7,3e-5],"Transform","log"), ... + optimizableVariable("dc_tracking_power_exponent",[0,2]), ... + optimizableVariable("dc_tracking_mu_eff_max",[1e-3,3e-1],"Transform","log")]; end - obj.dc_optimization_iter = 0; + obj.dc_tracking_optimization_iter = 0; fprintf("FFE DC opt uses fixed mu_tr=%9.3e, mu_dd=%9.3e on %d samples / %d symbols\n", ... obj.mu_tr,obj.mu_dd,N_opt,numel(d_opt)); - obj.dc_optimization = bayesopt(@(p)obj.dcObjective(p,x_opt,d_opt),vars, ... - "MaxObjectiveEvaluations",obj.dc_optimization_max_evals, ... + obj.dc_tracking_optimization = bayesopt(@(p)obj.dcTrackingObjective(p,x_opt,d_opt),vars, ... + "MaxObjectiveEvaluations",obj.dc_tracking_optimization_max_evals, ... "AcquisitionFunctionName","expected-improvement-plus", ... "IsObjectiveDeterministic",false, ... "Verbose",0, ... "PlotFcn",[]); - best = obj.dc_optimization.XAtMinObjective; - obj.mu_dc = best.mu_dc; - if obj.adaptive_dc_enabled - obj.dc_alpha = best.dc_alpha; - obj.dc_gamma = best.dc_gamma; - obj.dc_power_exponent = best.dc_power_exponent; - obj.dc_mu_eff_max = best.dc_mu_eff_max; - fprintf("\nFFE DC opt done: mu_dc=%9.3e, alpha=%6.3f, gamma=%9.3e, p=%5.2f, mu_eff_max=%9.3e, objective=%9.3e\n", ... - obj.mu_dc,obj.dc_alpha,obj.dc_gamma,obj.dc_power_exponent,obj.dc_mu_eff_max,obj.dc_optimization.MinObjective); + best = obj.dc_tracking_optimization.XAtMinObjective; + obj.dc_tracking_mu = best.dc_tracking_mu; + if obj.dc_tracking_adaptive_enabled + obj.dc_tracking_alpha = best.dc_tracking_alpha; + obj.dc_tracking_gamma = best.dc_tracking_gamma; + obj.dc_tracking_power_exponent = best.dc_tracking_power_exponent; + obj.dc_tracking_mu_eff_max = best.dc_tracking_mu_eff_max; + fprintf("\nFFE DC opt done: dc_tracking_mu=%9.3e, alpha=%6.3f, gamma=%9.3e, p=%5.2f, mu_eff_max=%9.3e, objective=%9.3e\n", ... + obj.dc_tracking_mu,obj.dc_tracking_alpha,obj.dc_tracking_gamma,obj.dc_tracking_power_exponent,obj.dc_tracking_mu_eff_max,obj.dc_tracking_optimization.MinObjective); else - fprintf("\nFFE DC opt done: mu_dc=%9.3e, objective=%9.3e\n", ... - obj.mu_dc,obj.dc_optimization.MinObjective); + fprintf("\nFFE DC opt done: dc_tracking_mu=%9.3e, objective=%9.3e\n", ... + obj.dc_tracking_mu,obj.dc_tracking_optimization.MinObjective); end end + function optimizeA2LevelWeights(obj,x,d) + if obj.dc_level_avg_bufferlength_a2 <= 1 + warning("FFE:A2OptimizationDisabled", ... + "A2 level-weight optimization requires dc_level_avg_bufferlength_a2 > 1."); + return + end + if obj.a2_level_weight_max <= 0 + warning("FFE:A2OptimizationDisabled", ... + "A2 level-weight optimization requires a2_level_weight_max > 0."); + return + end + if isempty(obj.constellation) + obj.constellation = unique(d); + end + + [x_opt,d_opt,N_opt] = obj.optimizationSignals(x,d,obj.a2_level_weight_optimization_len); + n_levels = numel(obj.constellation); + var_names = compose("dc_level_weight_a2_%d",1:n_levels); + vars_cell = cell(1,n_levels); + for level_idx = 1:n_levels + vars_cell{level_idx} = optimizableVariable(var_names(level_idx),[0,obj.a2_level_weight_max]); + end + vars = [vars_cell{:}]; + + [initial_weights,stats] = obj.a2LevelWeightInitialGuess(x_opt,d_opt); + current_weights = obj.expandA2LevelWeights(n_levels); + initial_matrix = initial_weights(:).'; + if any(current_weights ~= 0) + initial_matrix = [initial_matrix; current_weights(:).']; + end + initial_matrix = min(max(initial_matrix,0),obj.a2_level_weight_max); + initial_matrix = unique(initial_matrix,"rows","stable"); + initial_x = array2table(initial_matrix,"VariableNames",cellstr(var_names)); + obj.a2_level_weight_initial_stats = stats; + + obj.a2_level_weight_optimization_iter = 0; + max_evals = max(obj.a2_level_weight_optimization_max_evals,height(initial_x)); + fprintf("FFE A2 opt uses fixed mu_tr=%9.3e, mu_dd=%9.3e on %d samples / %d symbols\n", ... + obj.mu_tr,obj.mu_dd,N_opt,numel(d_opt)); + fprintf("FFE A2 opt init: var_slope=%9.3e, weights=%s\n", ... + stats.variance_slope,mat2str(initial_weights(:).',3)); + + obj.a2_level_weight_optimization = bayesopt(@(p)obj.a2LevelWeightObjective(p,x_opt,d_opt),vars, ... + "MaxObjectiveEvaluations",max_evals, ... + "InitialX",initial_x, ... + "AcquisitionFunctionName","expected-improvement-plus", ... + "IsObjectiveDeterministic",false, ... + "Verbose",0, ... + "PlotFcn",[]); + + best = obj.a2_level_weight_optimization.XAtMinObjective; + obj.dc_level_weights_a2 = obj.a2LevelWeightsFromParams(best); + fprintf("\nFFE A2 opt done: weights=%s, BER=%9.3e\n", ... + mat2str(obj.dc_level_weights_a2(:).',3),obj.a2_level_weight_optimization.MinObjective); + end + function [x_opt,d_opt,N_opt] = optimizationSignals(obj,x,d,opt_len) if nargin < 4 opt_len = obj.mu_optimization_len; @@ -469,20 +721,22 @@ classdef FFE < handle function objective = muObjective(obj,params,x,d) old_debug = obj.save_debug; - old_mu_dc = obj.mu_dc; + old_dc_tracking_mu = obj.dc_tracking_mu; obj.save_debug = 0; - has_mu_dc = any(strcmp(params.Properties.VariableNames,"mu_dc")); - if has_mu_dc - obj.mu_dc = params.mu_dc; + has_dc_tracking_mu = any(strcmp(params.Properties.VariableNames,"dc_tracking_mu")); + if has_dc_tracking_mu + obj.dc_tracking_mu = params.dc_tracking_mu; end obj.e = zeros(obj.order,1); obj.e_dc = 0; obj.P = (1/0.05) * eye(obj.order); obj.debug_struct = struct(); N_tr = min(obj.len_tr,numel(x)); - [signal,~] = obj.equalize(x,d,params.mu_tr,obj.epochs_tr,N_tr,1,0); + obj.equalize(x,d,params.mu_tr,obj.epochs_tr,N_tr,1,0); if obj.dd_mode [signal,~] = obj.equalize(x,d,params.mu_dd,obj.epochs_dd,numel(x),0,0); + else + [signal,~] = obj.equalize(x,d,0,1,numel(x),0,0); end [ber,errors] = obj.berObjective(signal,d); @@ -491,28 +745,58 @@ classdef FFE < handle objective = inf; end obj.mu_optimization_iter = obj.mu_optimization_iter + 1; - if obj.dd_mode && has_mu_dc - fprintf("\rFFE mu opt %02d: mu_tr=%9.3e, mu_dd=%9.3e, mu_dc=%9.3e, BER=%9.3e, errors=%d", ... - obj.mu_optimization_iter,params.mu_tr,params.mu_dd,params.mu_dc,ber,errors); + if obj.dd_mode && has_dc_tracking_mu + fprintf("\rFFE mu opt %02d: mu_tr=%9.3e, mu_dd=%9.3e, dc_tracking_mu=%9.3e, BER=%9.3e, errors=%d", ... + obj.mu_optimization_iter,params.mu_tr,params.mu_dd,params.dc_tracking_mu,ber,errors); elseif obj.dd_mode fprintf("\rFFE mu opt %02d: mu_tr=%9.3e, mu_dd=%9.3e, BER=%9.3e, errors=%d", ... obj.mu_optimization_iter,params.mu_tr,params.mu_dd,ber,errors); - elseif has_mu_dc - fprintf("\rFFE mu opt %02d: mu_tr=%9.3e, mu_dc=%9.3e, BER=%9.3e, errors=%d", ... - obj.mu_optimization_iter,params.mu_tr,params.mu_dc,ber,errors); + elseif has_dc_tracking_mu + fprintf("\rFFE mu opt %02d: mu_tr=%9.3e, dc_tracking_mu=%9.3e, BER=%9.3e, errors=%d", ... + obj.mu_optimization_iter,params.mu_tr,params.dc_tracking_mu,ber,errors); else fprintf("\rFFE mu opt %02d: mu_tr=%9.3e, BER=%9.3e, errors=%d", ... obj.mu_optimization_iter,params.mu_tr,ber,errors); end obj.save_debug = old_debug; - obj.mu_dc = old_mu_dc; + obj.dc_tracking_mu = old_dc_tracking_mu; end - function objective = dcObjective(obj,params,x,d) + function objective = a2LevelWeightObjective(obj,params,x,d) state = obj.captureObjectiveState(); cleanup = onCleanup(@()obj.restoreObjectiveState(state)); - obj.applyDcObjectiveParams(params); + obj.dc_level_weights_a2 = obj.a2LevelWeightsFromParams(params); + obj.save_debug = 0; + obj.e = zeros(obj.order,1); + obj.e_dc = 0; + obj.P = (1/0.05) * eye(obj.order); + obj.debug_struct = struct(); + + N_tr = min(obj.len_tr,numel(x)); + obj.equalize(x,d,obj.mu_tr,obj.epochs_tr,N_tr,1,0); + if obj.dd_mode + [signal,~] = obj.equalize(x,d,obj.mu_dd,obj.epochs_dd,numel(x),0,0); + else + [signal,~] = obj.equalize(x,d,0,1,numel(x),0,0); + end + + [ber,errors] = obj.berObjective(signal,d); + objective = ber; + if ~isfinite(objective) + objective = inf; + end + + obj.a2_level_weight_optimization_iter = obj.a2_level_weight_optimization_iter + 1; + fprintf("\rFFE A2 opt %02d: weights=%s, BER=%9.3e, errors=%d", ... + obj.a2_level_weight_optimization_iter,mat2str(obj.dc_level_weights_a2(:).',3),ber,errors); + end + + function objective = dcTrackingObjective(obj,params,x,d) + state = obj.captureObjectiveState(); + cleanup = onCleanup(@()obj.restoreObjectiveState(state)); + + obj.applyDcTrackingObjectiveParams(params); obj.save_debug = 1; obj.e = zeros(obj.order,1); obj.e_dc = 0; @@ -520,60 +804,165 @@ classdef FFE < handle obj.debug_struct = struct(); N_tr = min(obj.len_tr,numel(x)); - [signal,~] = obj.equalize(x,d,obj.mu_tr,obj.epochs_tr,N_tr,1,0); + obj.equalize(x,d,obj.mu_tr,obj.epochs_tr,N_tr,1,0); if obj.dd_mode [signal,~] = obj.equalize(x,d,obj.mu_dd,obj.epochs_dd,numel(x),0,0); + else + [signal,~] = obj.equalize(x,d,0,1,numel(x),0,0); end [ber,errors] = obj.berObjective(signal,d); - [delay_symbols,delay_corr] = obj.dcDelayObjective(signal,d); - delay_penalty = obj.dc_optimization_delay_weight * abs(delay_symbols) / max(numel(d),1); + [delay_symbols,delay_corr] = obj.dcTrackingDelayObjective(signal,d); + delay_penalty = obj.dc_tracking_optimization_delay_weight * abs(delay_symbols) / max(numel(d),1); objective = ber + delay_penalty; if ~isfinite(objective) objective = inf; end - obj.dc_optimization_iter = obj.dc_optimization_iter + 1; - if obj.adaptive_dc_enabled - fprintf("\rFFE DC opt %02d: mu_dc=%9.3e, alpha=%6.3f, gamma=%9.3e, p=%5.2f, mu_eff_max=%9.3e, BER=%9.3e, delay=%7.0f, corr=%6.3f, obj=%9.3e, errors=%d", ... - obj.dc_optimization_iter,params.mu_dc,params.dc_alpha,params.dc_gamma,params.dc_power_exponent,params.dc_mu_eff_max, ... + obj.dc_tracking_optimization_iter = obj.dc_tracking_optimization_iter + 1; + if obj.dc_tracking_adaptive_enabled + fprintf("\rFFE DC opt %02d: dc_tracking_mu=%9.3e, alpha=%6.3f, gamma=%9.3e, p=%5.2f, mu_eff_max=%9.3e, BER=%9.3e, delay=%7.0f, corr=%6.3f, obj=%9.3e, errors=%d", ... + obj.dc_tracking_optimization_iter,params.dc_tracking_mu,params.dc_tracking_alpha,params.dc_tracking_gamma,params.dc_tracking_power_exponent,params.dc_tracking_mu_eff_max, ... ber,delay_symbols,delay_corr,objective,errors); else - fprintf("\rFFE DC opt %02d: mu_dc=%9.3e, BER=%9.3e, delay=%7.0f, corr=%6.3f, obj=%9.3e, errors=%d", ... - obj.dc_optimization_iter,params.mu_dc,ber,delay_symbols,delay_corr,objective,errors); + fprintf("\rFFE DC opt %02d: dc_tracking_mu=%9.3e, BER=%9.3e, delay=%7.0f, corr=%6.3f, obj=%9.3e, errors=%d", ... + obj.dc_tracking_optimization_iter,params.dc_tracking_mu,ber,delay_symbols,delay_corr,objective,errors); end end - function applyDcObjectiveParams(obj,params) + function applyDcTrackingObjectiveParams(obj,params) var_names = string(params.Properties.VariableNames); - if any(var_names == "mu_dc") - obj.mu_dc = params.mu_dc; + if any(var_names == "dc_tracking_mu") + obj.dc_tracking_mu = params.dc_tracking_mu; end - if any(var_names == "dc_alpha") - obj.dc_alpha = params.dc_alpha; + if any(var_names == "dc_tracking_alpha") + obj.dc_tracking_alpha = params.dc_tracking_alpha; end - if any(var_names == "dc_gamma") - obj.dc_gamma = params.dc_gamma; + if any(var_names == "dc_tracking_gamma") + obj.dc_tracking_gamma = params.dc_tracking_gamma; end - if any(var_names == "dc_power_exponent") - obj.dc_power_exponent = params.dc_power_exponent; + if any(var_names == "dc_tracking_power_exponent") + obj.dc_tracking_power_exponent = params.dc_tracking_power_exponent; end - if any(var_names == "dc_mu_eff_max") - obj.dc_mu_eff_max = params.dc_mu_eff_max; + if any(var_names == "dc_tracking_mu_eff_max") + obj.dc_tracking_mu_eff_max = params.dc_tracking_mu_eff_max; end end + function weights = a2LevelWeightsFromParams(~,params) + var_names = string(params.Properties.VariableNames); + weight_names = var_names(startsWith(var_names,"dc_level_weight_a2_")); + weight_idx = extractAfter(weight_names,"dc_level_weight_a2_"); + weight_idx = str2double(weight_idx); + [weight_idx,sort_idx] = sort(weight_idx); + weight_names = weight_names(sort_idx); + weights = zeros(numel(weight_names),1); + for n = 1:numel(weight_names) + weights(weight_idx(n),1) = params.(char(weight_names(n))); + end + end + + function weights = expandA2LevelWeights(obj,n_levels) + if isscalar(obj.dc_level_weights_a2) + weights = repmat(obj.dc_level_weights_a2,n_levels,1); + elseif numel(obj.dc_level_weights_a2) == n_levels + weights = obj.dc_level_weights_a2(:); + else + builtin("error","FFE:InvalidDCLevelWeights", ... + "dc_level_weights_a2 must be scalar or have one entry per constellation level."); + end + weights = min(max(weights,0),obj.a2_level_weight_max); + end + + function [weights,stats] = a2LevelWeightInitialGuess(obj,x,d) + levels = obj.constellation(:); + if isempty(levels) + levels = unique(d(:)); + end + n_levels = numel(levels); + n_symbols = min(numel(d),floor(numel(x) / obj.sps)); + d = d(1:n_symbols); + rx_symbols = x(1:obj.sps:obj.sps*n_symbols); + rx_symbols = rx_symbols(:); + + level_mean = NaN(n_levels,1); + level_variance = NaN(n_levels,1); + for level_idx = 1:n_levels + level_samples = rx_symbols(d == levels(level_idx)); + level_mean(level_idx) = mean(level_samples,"omitnan"); + level_variance(level_idx) = var(level_samples,0,"omitnan"); + end + + valid = isfinite(level_mean) & isfinite(level_variance); + variance_fit = level_variance; + slope_axis = "mean"; + if nnz(valid) >= 2 + if numel(unique(level_mean(valid))) >= 2 + p = polyfit(level_mean(valid),level_variance(valid),1); + variance_fit = polyval(p,level_mean); + else + slope_axis = "level"; + level_axis = (1:n_levels).'; + p = polyfit(level_axis(valid),level_variance(valid),1); + variance_fit = polyval(p,(1:n_levels).'); + end + else + p = [0, mean(level_variance(valid),"omitnan")]; + end + + if any(valid) + baseline = min(variance_fit(valid),[],"omitnan"); + else + baseline = 0; + end + variance_score = max(variance_fit - baseline,0); + finite_score = isfinite(variance_score); + if ~any(finite_score) || max(variance_score(finite_score),[],"omitnan") <= 0 + if any(valid) + baseline = min(level_variance(valid),[],"omitnan"); + else + baseline = 0; + end + variance_score = max(level_variance - baseline,0); + finite_score = isfinite(variance_score); + end + + weights = zeros(n_levels,1); + usable_score = valid & finite_score; + if any(usable_score) + max_score = max(variance_score(usable_score),[],"omitnan"); + else + max_score = 0; + end + if isfinite(max_score) && max_score > 0 + initial_weight_ceiling = min(0.7,obj.a2_level_weight_max); + weights(usable_score) = initial_weight_ceiling * variance_score(usable_score) ./ max_score; + end + weights = min(max(weights,0),obj.a2_level_weight_max); + + stats = struct( ... + "levels",levels, ... + "mean",level_mean, ... + "variance",level_variance, ... + "variance_fit",variance_fit(:), ... + "variance_slope",p(1), ... + "slope_axis",slope_axis, ... + "initial_weights",weights); + end + function state = captureObjectiveState(obj) state.e = obj.e; state.e_dc = obj.e_dc; state.P = obj.P; state.save_debug = obj.save_debug; state.debug_struct = obj.debug_struct; - state.mu_dc = obj.mu_dc; - state.dc_alpha = obj.dc_alpha; - state.dc_gamma = obj.dc_gamma; - state.dc_power_exponent = obj.dc_power_exponent; - state.dc_mu_eff_max = obj.dc_mu_eff_max; + state.dc_tracking_mu = obj.dc_tracking_mu; + state.dc_tracking_alpha = obj.dc_tracking_alpha; + state.dc_tracking_gamma = obj.dc_tracking_gamma; + state.dc_tracking_power_exponent = obj.dc_tracking_power_exponent; + state.dc_tracking_mu_eff_max = obj.dc_tracking_mu_eff_max; + state.dc_level_weights_a2 = obj.dc_level_weights_a2; + state.a2_level_weight_initial_stats = obj.a2_level_weight_initial_stats; end function restoreObjectiveState(obj,state) @@ -582,11 +971,13 @@ classdef FFE < handle obj.P = state.P; obj.save_debug = state.save_debug; obj.debug_struct = state.debug_struct; - obj.mu_dc = state.mu_dc; - obj.dc_alpha = state.dc_alpha; - obj.dc_gamma = state.dc_gamma; - obj.dc_power_exponent = state.dc_power_exponent; - obj.dc_mu_eff_max = state.dc_mu_eff_max; + obj.dc_tracking_mu = state.dc_tracking_mu; + obj.dc_tracking_alpha = state.dc_tracking_alpha; + obj.dc_tracking_gamma = state.dc_tracking_gamma; + obj.dc_tracking_power_exponent = state.dc_tracking_power_exponent; + obj.dc_tracking_mu_eff_max = state.dc_tracking_mu_eff_max; + obj.dc_level_weights_a2 = state.dc_level_weights_a2; + obj.a2_level_weight_initial_stats = state.a2_level_weight_initial_stats; end function [ber,errors] = berObjective(~,signal,d) @@ -603,23 +994,23 @@ classdef FFE < handle "returnErrorLocation",1); end - function [delay_symbols,delay_corr] = dcDelayObjective(obj,signal,d) + function [delay_symbols,delay_corr] = dcTrackingDelayObjective(obj,signal,d) delay_symbols = 0; delay_corr = 0; - if ~isfield(obj.debug_struct,"e_dc_eff") || isempty(obj.debug_struct.e_dc_eff) + if ~isfield(obj.debug_struct,"dc_tracking_est") || isempty(obj.debug_struct.dc_tracking_est) return end - smooth_len = obj.dc_optimization_smoothing_len; - e_dc_eff_s = movmean(obj.debug_struct.e_dc_eff(:),smooth_len,"omitnan"); + smooth_len = obj.dc_tracking_optimization_smoothing_len; + dc_tracking_est_s = movmean(obj.debug_struct.dc_tracking_est(:),smooth_len,"omitnan"); avg_lvl_dc = obj.averageLevelTrace(signal,d,smooth_len); - xcorr_len = min(numel(e_dc_eff_s),numel(avg_lvl_dc)); + xcorr_len = min(numel(dc_tracking_est_s),numel(avg_lvl_dc)); if xcorr_len < 2 return end - inv_dc_xcorr = -e_dc_eff_s(1:xcorr_len); + inv_dc_xcorr = -dc_tracking_est_s(1:xcorr_len); avg_lvl_xcorr = avg_lvl_dc(1:xcorr_len); inv_dc_xcorr = fillmissing(inv_dc_xcorr,"linear","EndValues","nearest"); avg_lvl_xcorr = fillmissing(avg_lvl_xcorr,"linear","EndValues","nearest"); diff --git a/Functions/EQ_blocks/ffe.m b/Functions/EQ_blocks/ffe.m index 0cbd42b..07ea93b 100644 --- a/Functions/EQ_blocks/ffe.m +++ b/Functions/EQ_blocks/ffe.m @@ -74,17 +74,22 @@ end % Create FFE results structure ffe_results = struct(); -try - eq_.e = []; - eq_.e2 = []; - eq_.e3 = []; - eq_.b = []; - eq_.b2 = []; - eq_.b3 = []; -end -try - eq_.mu_optimization = []; - eq_.dc_optimization = []; +config_clear_props = { ... + "e", ... + "e2", ... + "e3", ... + "b", ... + "b2", ... + "b3", ... + "mu_optimization", ... + "dc_optimization", ... + "dc_tracking_optimization", ... + "a2_level_weight_optimization"}; +for config_clear_idx = 1:numel(config_clear_props) + prop_name = config_clear_props{config_clear_idx}; + if isprop(eq_,prop_name) + eq_.(prop_name) = []; + end end ffe_results.config = Equalizerstruct(); diff --git a/Functions/EQ_recipes/dsp_recipe_minimal.m b/Functions/EQ_recipes/dsp_recipe_minimal.m index fca662b..9b1609b 100644 --- a/Functions/EQ_recipes/dsp_recipe_minimal.m +++ b/Functions/EQ_recipes/dsp_recipe_minimal.m @@ -21,7 +21,7 @@ function output = dsp_recipe_minimal(Scpe_sig_raw, Symbols, Tx_bits, options) "debug_plots", options.debug_plots); eq_ffe = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-1,"mu_tr",0.4,"order",25,... - "sps",2,"decide",0,"optmize_mus",0,"dd_mode",options.userParameters.dd_mode,"adaption_technique","nlms","mu_dc",1.021e-05); + "sps",2,"decide",0,"optmize_mus",0,"dd_mode",options.userParameters.dd_mode,"adaption_technique","nlms","dc_tracking_mu",1.021e-05); ffe_results = ffe(eq_ffe, options.M, Scpe_sig, Symbols, Tx_bits, ... "precode_mode", options.duob_mode, ... diff --git a/Functions/EQ_recipes/dsp_scope_signal.m b/Functions/EQ_recipes/dsp_scope_signal.m index e2d4f68..8e42ae6 100644 --- a/Functions/EQ_recipes/dsp_scope_signal.m +++ b/Functions/EQ_recipes/dsp_scope_signal.m @@ -70,8 +70,8 @@ function output = dsp_scope_signal(Scpe_sig_raw, Symbols, Tx_bits, options) pf_ = Postfilter("ncoeff", pf_ncoeffs, "useBurg", 1); %#ok mlse_ = MLSE("duobinary_output", 0, 'M', M, 'trellis_states', PAMmapper(M,0).levels); %#ok - 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","mu_dc",mu_dc); %#ok - 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_technique",adaption_method(adaption),"dd_mode",use_dd_mode,"mu_dc",mu_dc); %#ok + 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","dc_tracking_mu",mu_dc); %#ok + 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_technique",adaption_method(adaption),"dd_mode",use_dd_mode,"dc_tracking_mu",mu_dc); %#ok mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels); %#ok eq_db_enc = EQ("Ne", ffe_order_dbtgt, "Nb", dfe_order_dbtgt, "training_length", len_tr, ... @@ -94,7 +94,7 @@ function output = dsp_scope_signal(Scpe_sig_raw, Symbols, Tx_bits, options) if use_ffe eq_ffe = EQ("Ne",ffe_order_ffe,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); eq_ffe = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-1,"mu_tr",0.4,"order",ffe_order_ffe(1),... - "sps",2,"decide",0,"optmize_mus",0,"dd_mode",1,"adaption_technique","nlms","mu_dc",1.021e-05); + "sps",2,"decide",0,"optmize_mus",0,"dd_mode",1,"adaption_technique","nlms","dc_tracking_mu",1.021e-05); ffe_results = ffe(eq_ffe, M, Scpe_sig, Symbols, Tx_bits, ... "precode_mode", duob_mode, ... diff --git a/Functions/EQ_recipes/mpi_recipe_dev.m b/Functions/EQ_recipes/mpi_recipe_dev.m index ea7c5fb..bb0fc13 100644 --- a/Functions/EQ_recipes/mpi_recipe_dev.m +++ b/Functions/EQ_recipes/mpi_recipe_dev.m @@ -21,23 +21,61 @@ function output = mpi_recipe_dev(Scpe_sig_raw, Symbols, Tx_bits, options) "debug_plots", options.debug_plots); output = struct(); - eq_settings = { ... - "epochs_tr", 5, ... - "epochs_dd", 5, ... - "len_tr", 4096, ... - "mu_tr",0.04, ... - "mu_dd",0.012, ... - "mu_dc", 0, ... - "adaptive_dc_enabled", 0, ... - "order", 25, ... + eq_core_settings = { ... "sps", 2, ... + "order", 25, ... "decide", 0, ... - "optmize_mus", 0, ... + "adaption_technique", "nlms"}; + + eq_training_settings = { ... + "len_tr", 4096, ... + "epochs_tr", 5, ... + "mu_tr", 0.04}; + + eq_dd_settings = { ... "dd_mode", 1, ... - "adaption_technique", "nlms", ... - "plot_mu_optimization", options.debug_plots,... - "optimize_dc_params", false, ... - "save_debug",true}; + "epochs_dd", 5, ... + "mu_dd", 0.012}; + + eq_a1_settings = { ... + "dc_smoothing_a1", 0, ... + "dc_avg_bufferlength_a1", 0}; + + eq_a2_settings = { ... + "dc_smoothing_a2", 0, ... + "dc_level_avg_bufferlength_a2", 0, ... + "dc_level_weights_a2", [0.692 0.979 0.98 0.138]}; + + eq_dc_tracking_settings = { ... + "dc_tracking_mu", 0.05, ... + "dc_tracking_adaptive_enabled", 0, ... + "dc_tracking_buffer_len", 1024}; + + eq_ffe_update_settings = { ... + "ffe_update_buffer_len", 1}; + + eq_optimizer_settings = { ... + "optmize_mus", 0, ... + "plot_mu_optimization", options.debug_plots, ... + "optimize_dc_tracking_params", 1, ... + "optimize_a2_level_weights", 0, ... + "a2_level_weight_optimization_len", 2^15, ... + "a2_level_weight_optimization_max_evals", 30, ... + "a2_level_weight_max", 1}; + + eq_debug_settings = { ... + "save_debug", true}; + + eq_settings = [ ... + eq_core_settings, ... + eq_training_settings, ... + eq_dd_settings, ... + eq_a1_settings, ... + eq_a2_settings, ... + eq_dc_tracking_settings, ... + eq_ffe_update_settings, ... + eq_optimizer_settings, ... + eq_debug_settings]; eq_ffe = FFE(eq_settings{:}); @@ -73,8 +111,8 @@ function output = mpi_recipe_dev(Scpe_sig_raw, Symbols, Tx_bits, options) output.ffe_debug.smoothing_window = smooth_len; error_first_s = movmean(dbg.error_first_epoch(:),smooth_len,"omitnan"); error_last_s = movmean(dbg.error(:),smooth_len,"omitnan"); - mu_dc_eff_s = movmean(dbg.mu_dc_eff(:),smooth_len,"omitnan"); - e_dc_eff_s = movmean(dbg.e_dc_eff(:),smooth_len,"omitnan"); + dc_tracking_mu_eff_s = movmean(dbg.dc_tracking_mu_eff(:),smooth_len,"omitnan"); + dc_tracking_est_s = movmean(dbg.dc_tracking_est(:),smooth_len,"omitnan"); figure(460); clf; tiledlayout(2,1,"TileSpacing","compact","Padding","compact"); @@ -90,9 +128,9 @@ function output = mpi_recipe_dev(Scpe_sig_raw, Symbols, Tx_bits, options) nexttile;hold on % plot(eq_sig_mov,"DisplayName","err dc eff"); - plot(mu_dc_eff_s,"DisplayName","mu DC - either fixed or adaptive"); - e_dc_scale = max(abs(e_dc_eff_s),[],"omitnan") + eps; - plot(-1.*e_dc_eff_s./e_dc_scale,"DisplayName","Inverted DC-tracking; Value that is subtracted during EQ"); + plot(dc_tracking_mu_eff_s,"DisplayName","mu DC - either fixed or adaptive"); + e_dc_scale = max(abs(dc_tracking_est_s),[],"omitnan") + eps; + plot(-1.*dc_tracking_est_s./e_dc_scale,"DisplayName","Inverted DC-tracking; Value that is subtracted during EQ"); avg_lvl_dc=mean(avg_for_lvl,1,"omitnan"); plot(avg_lvl_dc./0.01,"DisplayName","Smoothed EQ output signal; calc'd by showLevelScatter"); grid on; @@ -101,7 +139,7 @@ function output = mpi_recipe_dev(Scpe_sig_raw, Symbols, Tx_bits, options) title("Effective adaptive DC step"); legend - inv_dc_track = -e_dc_eff_s(:); + inv_dc_track = -dc_tracking_est_s(:); avg_lvl_dc = avg_lvl_dc(:); xcorr_len = min(numel(inv_dc_track),numel(avg_lvl_dc)); inv_dc_xcorr = inv_dc_track(1:xcorr_len); @@ -135,33 +173,33 @@ function output = mpi_recipe_dev(Scpe_sig_raw, Symbols, Tx_bits, options) - - - %% MPI Reduction - if 1 - - % dc_buffer_len = 0; - % ffe_buffer_len = 0; - % smoothing_buffer_length = options.userParameters.smoothing_length; - % smoothing_buffer_update = 1; - - % eq_ffe_dcr = FFE_DCremoval_adaptive_mu(eq_settings{:}, ... - % "dc_buffer_len",dc_buffer_len, ... - % "ffe_buffer_len",ffe_buffer_len,... - % "smoothing_buffer_length",smoothing_buffer_length,... - % "smoothing_buffer_update",smoothing_buffer_update); - - eq_ = FFE_DCremoval("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",1); - - ffe_results_dcr = ffe(eq_, options.M, Scpe_sig, Symbols, Tx_bits, ... - "precode_mode", options.duob_mode, ... - 'showAnalysis', options.debug_plots, ... - "postFFE", [], ... - "eth_style_symbol_mapping", 0); - - ffe_results_dcr.metrics.print("description",'FFE DCR'); - output.ffe_dcr_package = ffe_results_dcr; - end + % + % + % %% MPI Reduction + % if 1 + % + % % dc_buffer_len = 0; + % % ffe_buffer_len = 0; + % % smoothing_buffer_length = options.userParameters.smoothing_length; + % % smoothing_buffer_update = 1; + % + % % eq_ffe_dcr = FFE_DCremoval_adaptive_mu(eq_settings{:}, ... + % % "dc_buffer_len",dc_buffer_len, ... + % % "ffe_buffer_len",ffe_buffer_len,... + % % "smoothing_buffer_length",smoothing_buffer_length,... + % % "smoothing_buffer_update",smoothing_buffer_update); + % + % eq_ = FFE_DCremoval("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",1); + % + % ffe_results_dcr = ffe(eq_, options.M, Scpe_sig, Symbols, Tx_bits, ... + % "precode_mode", options.duob_mode, ... + % 'showAnalysis', options.debug_plots, ... + % "postFFE", [], ... + % "eth_style_symbol_mapping", 0); + % + % ffe_results_dcr.metrics.print("description",'FFE DCR'); + % output.ffe_dcr_package = ffe_results_dcr; + % end end diff --git a/Tests/04_DSP/Equalizer/FFE_test.m b/Tests/04_DSP/Equalizer/FFE_test.m index 8b82541..3d9896d 100644 --- a/Tests/04_DSP/Equalizer/FFE_test.m +++ b/Tests/04_DSP/Equalizer/FFE_test.m @@ -12,8 +12,19 @@ classdef FFE_test < IMDDTestCase testCase.verifyEqual(ffe.dd_mode, 1); testCase.verifyEqual(ffe.mu_dd, 1e-5); testCase.verifyEqual(ffe.epochs_dd, 5); - testCase.verifyFalse(logical(ffe.optimize_dc_params)); - testCase.verifyEqual(ffe.dc_optimization_delay_weight, 1e-3); + testCase.verifyFalse(logical(ffe.optimize_dc_tracking_params)); + testCase.verifyEqual(ffe.dc_tracking_optimization_delay_weight, 1e-3); + testCase.verifyEqual(ffe.dc_tracking_power_exponent, 2); + testCase.verifyEqual(ffe.dc_avg_bufferlength_a1, 0); + testCase.verifyEqual(ffe.dc_smoothing_a1, 0); + testCase.verifyEqual(ffe.dc_level_avg_bufferlength_a2, 0); + testCase.verifyEqual(ffe.dc_smoothing_a2, 0); + testCase.verifyEqual(ffe.dc_level_weights_a2, 0); + testCase.verifyEqual(ffe.dc_tracking_buffer_len, 1); + testCase.verifyEqual(ffe.ffe_update_buffer_len, 1); + testCase.verifyFalse(logical(ffe.optimize_a2_level_weights)); + testCase.verifyEqual(ffe.a2_level_weight_optimization_max_evals, 30); + testCase.verifyEqual(ffe.a2_level_weight_max, 1); testCase.verifyFalse(ffe.decide); testCase.verifyEqual(ffe.e, zeros(ffe.order, 1)); testCase.verifyEqual(ffe.error, 0); @@ -71,6 +82,182 @@ classdef FFE_test < IMDDTestCase testCase.verifyNotEqual(y.signal, zeros(size(x.signal))); testCase.verifyEqual(length(y.signal), length(x.signal)); end + + function dcTrackingBufferDelaysDcUpdateUntilBlockBoundary(testCase) + x = zeros(4, 1); + d = ones(4, 1); + + ffe = FFE( ... + "sps", 1, ... + "order", 1, ... + "dc_tracking_mu", 0.1, ... + "dc_tracking_buffer_len", 2, ... + "dd_mode", 0, ... + "adaption_technique", adaption_method.lms); + + ffe.constellation = unique(d); + ffe.equalize(x, d, 0, 1, numel(x), true, false); + + testCase.verifyEqual(ffe.e_dc, 0.19, "AbsTol", 1e-12); + end + + function ffeBufferDelaysTapUpdateUntilBlockBoundary(testCase) + x = ones(4, 1); + d = ones(4, 1); + + ffe = FFE( ... + "sps", 1, ... + "order", 1, ... + "dc_tracking_mu", 0, ... + "ffe_update_buffer_len", 2, ... + "dd_mode", 0, ... + "adaption_technique", adaption_method.lms); + + ffe.constellation = unique(d); + ffe.equalize(x, d, 0.1, 1, numel(x), true, false); + + testCase.verifyEqual(ffe.e, 0.19, "AbsTol", 1e-12); + end + + function dcAvgA1BufferlengthOneLeavesInputUnchanged(testCase) + x = (1:4).'; + d = zeros(4, 1); + + ffe = FFE( ... + "sps", 1, ... + "order", 1, ... + "dc_avg_bufferlength_a1", 1, ... + "save_debug", true, ... + "dd_mode", 0, ... + "adaption_technique", adaption_method.lms); + ffe.e = 1; + + [y,~] = ffe.equalize(x, d, 0, 1, numel(x), true, false); + + testCase.verifyEqual(y, x, "AbsTol", 1e-12); + testCase.verifyEqual(ffe.debug_struct.dc_avg_offset, zeros(1,4), "AbsTol", 1e-12); + end + + function dcAvgA1SubtractsCausalMovingAverageByDefault(testCase) + x = (1:4).'; + d = zeros(4, 1); + expected_offset = [0, 0, 0, 2]; + expected_y = [1; 2; 3; 2]; + + ffe = FFE( ... + "sps", 1, ... + "order", 1, ... + "dc_avg_bufferlength_a1", 3, ... + "save_debug", true, ... + "dd_mode", 0, ... + "adaption_technique", adaption_method.lms); + ffe.e = 1; + + [y,~] = ffe.equalize(x, d, 0, 1, numel(x), true, false); + + testCase.verifyEqual(y, expected_y, "AbsTol", 1e-12); + testCase.verifyEqual(ffe.debug_struct.dc_avg_offset, expected_offset, "AbsTol", 1e-12); + end + + function dcAvgA1SmoothingIsReservedForOfflineVariants(testCase) + x = (1:4).'; + d = zeros(4, 1); + expected_offset = [0, 0, 0, 2]; + expected_y = [1; 2; 3; 2]; + + ffe = FFE( ... + "sps", 1, ... + "order", 1, ... + "dc_avg_bufferlength_a1", 3, ... + "dc_smoothing_a1", 0.5, ... + "save_debug", true, ... + "dd_mode", 0, ... + "adaption_technique", adaption_method.lms); + ffe.e = 1; + + [y,~] = ffe.equalize(x, d, 0, 1, numel(x), true, false); + + testCase.verifyEqual(y, expected_y, "AbsTol", 1e-12); + testCase.verifyEqual(ffe.debug_struct.dc_avg_offset, expected_offset, "AbsTol", 1e-12); + end + + function dcAvgA1WarnsWhenCombinedWithDcTracking(testCase) + testCase.verifyWarning(@() FFE( ... + "dc_avg_bufferlength_a1", 3, ... + "dc_tracking_mu", 0.1), "FFE:DCAvgWithDcTracking"); + end + + function dcLevelAvgA2SubtractsScalarWeightedResidual(testCase) + x = (1:4).'; + d = zeros(4, 1); + expected_y = [1; 2; 1.5; 2.5]; + + ffe = FFE( ... + "sps", 1, ... + "order", 1, ... + "dc_level_avg_bufferlength_a2", 2, ... + "dc_level_weights_a2", 1, ... + "save_debug", true, ... + "dd_mode", 0, ... + "adaption_technique", adaption_method.lms); + ffe.e = 1; + ffe.constellation = 0; + + [y,~] = ffe.equalize(x, d, 0, 1, numel(x), true, false); + + testCase.verifyEqual(y, expected_y, "AbsTol", 1e-12); + testCase.verifyEqual(ffe.debug_struct.dc_level_mpi_est, [0, 0, 1.5, 1.5], "AbsTol", 1e-12); + testCase.verifyEqual(ffe.debug_struct.dc_level_weight, [0, 0, 1, 1], "AbsTol", 1e-12); + testCase.verifyEqual(ffe.debug_struct.dc_level_valid_count, [0, 0, 2, 2]); + end + + function dcLevelAvgA2UsesVectorWeightsInConstellationOrder(testCase) + x = [1; 3; 1; 4]; + d = [0; 2; 0; 2]; + + ffe = FFE( ... + "sps", 1, ... + "order", 1, ... + "dc_level_avg_bufferlength_a2", 2, ... + "dc_level_weights_a2", [0, 1], ... + "dd_mode", 0, ... + "adaption_technique", adaption_method.lms); + ffe.e = 1; + ffe.constellation = [0; 2]; + + [y,~] = ffe.equalize(x, d, 0, 1, numel(x), true, false); + + testCase.verifyEqual(y, [1; 3; 1; 3.5], "AbsTol", 1e-12); + end + + function dcLevelAvgA2WarnsWhenCombinedWithDcTracking(testCase) + testCase.verifyWarning(@() FFE( ... + "dc_level_avg_bufferlength_a2", 2, ... + "dc_level_weights_a2", 1, ... + "dc_tracking_mu", 0.1), "FFE:DCLevelAvgWithOtherDcSuppression"); + end + + function a2InitialWeightGuessFollowsLevelVarianceSlope(testCase) + x = [ ... + 0.98; 1.02; 1.00; 1.01; ... + 1.90; 2.10; 2.00; 2.08; ... + 2.50; 3.50; 3.00; 3.40]; + d = [ ... + ones(4,1); ... + 2*ones(4,1); ... + 3*ones(4,1)]; + + ffe = FFE("sps", 1, "a2_level_weight_max", 1); + ffe.constellation = [1; 2; 3]; + + [weights,stats] = ffe.a2LevelWeightInitialGuess(x,d); + + testCase.verifySize(weights, [3, 1]); + testCase.verifyGreaterThan(weights(3), weights(2)); + testCase.verifyGreaterThan(weights(2), weights(1)); + testCase.verifyEqual(max(weights), 0.7, "AbsTol", 1e-12); + testCase.verifyGreaterThan(stats.variance_slope, 0); + end end end diff --git a/projects/Diss/MPI_revisit/investigate_mpi_algorithms.m b/projects/Diss/MPI_revisit/investigate_mpi_algorithms.m index 9d5633d..5eb1a0f 100644 --- a/projects/Diss/MPI_revisit/investigate_mpi_algorithms.m +++ b/projects/Diss/MPI_revisit/investigate_mpi_algorithms.m @@ -4,7 +4,7 @@ dsp_options.mode = "run_id"; dsp_options.recipe = @mpi_recipe_dev; dsp_options.append_to_db = false; dsp_options.start_occurence = 2; -dsp_options.max_occurences = 10; +dsp_options.max_occurences = 1; dsp_options.debug_plots = false; dsp_options.database_type = "mysql"; @@ -28,7 +28,7 @@ fp.where('Runs','run_id','GREATER_EQUAL',3153); fp.where('Runs', 'symbolrate', 'EQUALS', 112e9); fp.where('Runs', 'fiber_length', 'EQUALS', 0); fp.where('Runs', 'interference_path_length', 'EQUALS', 1000); -fp.where('Runs', 'sir', 'LESS_EQUAL', 30); +fp.where('Runs', 'sir', 'LESS_EQUAL', 20); fp.where('Runs', 'db_mode', 'EQUALS', '"no_db"'); % fp.where('Runs', 'is_mpi', 'EQUALS', 1); fp.where('Runs', 'pam_level', 'EQUALS', 4); @@ -41,6 +41,7 @@ fp.where('Runs', 'pam_level', 'EQUALS', 4); [~, sortIdx] = sort(dataTable.sir, 'descend'); dataTable = dataTable(sortIdx, :); +dataTable = dataTable(1, :); run_ids = dataTable.run_id; if isempty(run_ids) @@ -74,31 +75,43 @@ fprintf("-> [ %d run_id(s) × %d userParam combination(s) = %d parallel job(s) ] %% Analyze storageNames = fieldnames(wh.sto); -x_vars = run_ids;%dsp_options.userParameters.smoothing_length; -x_vars = reshape(x_vars,1,[]); +result = wh.sto.(storageNames{1}); +result = result(:).'; -figure(2026);hold on -for i = 1:numel(run_ids) - disp(run_ids(i)) - result = wh.getStoValue(storageNames{1}, x_vars); - % Each cell in result is a cell array (packageCell) containing multiple packages. - % Extract BERs from all packages and, for example, average them per x_var. - ber_all = cellfun(@(packageCell) cellfun(@(pkg) pkg.metrics.BER, packageCell), result, 'UniformOutput', false); - % Convert to numeric matrix (rows = packages, cols = x_vars) by padding if needed - maxPackages = max(cellfun(@numel, ber_all)); - ber_mat = nan(maxPackages, numel(ber_all)); - for k = 1:numel(ber_all) - ber_mat(1:numel(ber_all{k}), k) = ber_all{k}; +% Each stored entry is a package cell containing one or more occurrences. +ber_all = cell(1,numel(result)); +for result_idx = 1:numel(result) + packageCell = result{result_idx}; + if isempty(packageCell) + ber_all{result_idx} = NaN; + continue end - % Choose aggregation: mean across packages (ignore NaNs) - ber = mean(ber_mat, 1, 'omitnan'); - - - x = dataTable.sir; - y = ber; - - plot(x,ber); - scatter(x,ber_mat) - beautifyBERplot("logscale",true,"setcolors",false,"setmarkers",true); + + ber_values = nan(1,numel(packageCell)); + for package_idx = 1:numel(packageCell) + if isstruct(packageCell{package_idx}) && isfield(packageCell{package_idx},"metrics") + ber_values(package_idx) = packageCell{package_idx}.metrics.BER; + end + end + ber_all{result_idx} = ber_values; end +maxPackages = max(cellfun(@numel,ber_all)); +ber_mat = nan(maxPackages,numel(ber_all)); +for k = 1:numel(ber_all) + ber_mat(1:numel(ber_all{k}),k) = ber_all{k}; +end +ber = mean(ber_mat,1,"omitnan"); + +x = dataTable.sir(:).'; +if numel(x) ~= numel(ber) + x = 1:numel(ber); +end + +figure(2026); clf; hold on +plot(x,ber); +for k = 1:numel(x) + scatter(repmat(x(k),maxPackages,1),ber_mat(:,k)) +end +beautifyBERplot("logscale",true,"setcolors",false,"setmarkers",true); + diff --git a/projects/Diss/MPI_revisit/workerError.mat b/projects/Diss/MPI_revisit/workerError.mat new file mode 100644 index 0000000000000000000000000000000000000000..9fb24f6dac4a32d073e0dc81c45b7225ccb92f21 GIT binary patch literal 1358 zcmV-U1+n@~K~zjZLLfCRFd$7qR4ry{Y-KDUP;6mzW^ZzBIv`L(S4mDbG%O%Pa%Ew3 zWn>_4ZaN@TXmub;b!;FYIUq4NIx;jmG%_GEFfukEARr(hARr(hARr(hARr(hARr(h zARr(hARr(hARr(hARr(hARr*w00000000000ZB~{0000{0001Zoa19)VCVp1HX!Bz zVnGH7U}Ruo@O1^zEDXNR{=tkuF)kqXb*)HFEhx#%&x5Gr17Z&({k%~9tWZ81*enQU zfbu0E%)3lTaxe@4(fJ4v4*&oFZ3F-Sc%1E7-EP}96s8?7jf*y2Z#T5nUAv1VU4Ru? zhGB00uoUx}&hfeq0z6uxY_^gph*Xf?<+7K%+#~d|kB~>{BlHnQN|a5rlBGyVgDyD$ zAsvb5@H;=`p~PhXz{yC)V3&}Tl`{!-YR--%l?hYwH5aj{7v3=v5TIEFbGgM7)bfCB0n83;csX-;_({! zb{{on`#0sY7QbE>KH%-0Z0~v7=kNZOZOj{c9Uw48ERzk z>a!*AvHeZSFDUO?^4gTXOW!Xl@2`Pryi>i4CGZ6bpk?Ly4&=u<#iGV}UFpA=(Op+?VS?jr`Ywd0LQ`%js?Kw>*ZbN zc}K{Vhm!5{Bcb0zP#%w}njpfYI z?a9Kkq~Q^Ks-|9^{B;5INAm}bh8|}8AuTFQZG_RGHoI2;X!Q^7Am zNaA{8Zez+j?eFnb;1E;HOza;b%J2v>G!Ux;bVnq_Z0yC3DpM!F((nmBU+Wq8?c`bP zw{Zckbq&{g;y0cYqY!$A*s@Oi_50exwMWoogt$X;Wb%sY!lCKHWG1b6Rq75GteGD# zVmsg8%&+5ZOT&y_ zQ-nJu)qknISNn|YeMa?o?3`;nF6;iLdOW^A*LYmEeO2{1c0QwZ>hsypRhR#+Ry?lu zk=6E*7ZtBhEmz&nvym@~|JNFB!RN>S$BEv{@*Pn3zv)$;mXiV|8@@E^q;?q zSB*>7xfGbu(>hoA8wGfE`{VlB{IvEyXwH|{zx&hgupyA=T@%VZ_~MkFeI+z|j5Tu} zrX7eWI6=N&Qc~?w^R3;SZ&y>^UBo(cTD#`OTkn6`{4OJ}?#HvvjsFCyUlU$EH~wYL Q_*waucWx}`KUTH?w|+L1@c;k- literal 0 HcmV?d00001 diff --git a/projects/Diss/investigate_channel_memory.m b/projects/Diss/investigate_channel_memory.m index 517b686..cbcf496 100644 --- a/projects/Diss/investigate_channel_memory.m +++ b/projects/Diss/investigate_channel_memory.m @@ -133,7 +133,7 @@ for i = 1:numel(fsym_values) eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr, ... "mu_dd",1e-3,"mu_tr",0.4,"order",50, ... "sps",2,"decide",0,"optmize_mus",0,"dd_mode",1, ... - "adaption_technique","nlms","mu_dc",1e-3); + "adaption_technique","nlms","dc_tracking_mu",1e-3); pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); diff --git a/projects/Diss/investigate_noise_enhancement.m b/projects/Diss/investigate_noise_enhancement.m index 68dd625..762ac77 100644 --- a/projects/Diss/investigate_noise_enhancement.m +++ b/projects/Diss/investigate_noise_enhancement.m @@ -134,7 +134,7 @@ for i = 1:numel(fsym_values) eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr, ... "mu_dd",1e-1,"mu_tr",0.4,"order",50, ... "sps",2,"decide",0,"optmize_mus",1,"dd_mode",1, ... - "adaption_technique","nlms","mu_dc",1.021e-05); + "adaption_technique","nlms","dc_tracking_mu",1.021e-05); case "VNLE" eq_ = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr, ... "mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0, ... diff --git a/workerError.mat b/workerError.mat index a8ca784f44c5722921e17c45cf51f30cd6ff9170..e62af78533bf3e797bb0d8a2f899dd50ec585956 100644 GIT binary patch delta 1071 zcmV+~1kn4!3g-!sKnyoJGBG+aF(5K9GB%M>BavVQk#uQ)1_S^Ac%1E7&2Q5%6nC@I ztphf=!8p((7Z4yJ!7g;4ViSc;T>+s;!A+djEMJP9Y20??$dNx`#~nHHXK>_C;K&g> zaaPw$nylTJ_;`}#Jjd_l_de|Bx^n=4YqtQXv3`cNI!Ne};47?sUr2gRn&3fYqs93- z)|v|gc`ZYKK5fZ(HGWqSb6P7e+o_(PHwSvIi}{t~z8cTv{8iT0v4dWRFbGg+7()Jw zBtQ6G!e3Ev@cD<8e7pB6!}B#UR%QJxYoAy<674%{TkPFC(fX{>4*>#wNR|=uR$$cY zQ-CexQEX!r0v8bidk9#_hLJ-Fh2}0mULSfUvJwt|hP}vT0tBr1VTeqMeGjzO_DvMT zk0y)=0-03L9v-4f1}~4zfRpxXf}fJ!RYk7}-=*(s()(#JhDRsvrj0h*KgBak2G z6pI|^$I|>-#(de%x)@LB$J02!CHZQG{H!E@P4MrD?;+3hEi}NfRiNj#qKEUXrJd(X z%Zprp-D1ZhQ4shcMb;{$aEGTD*LzT)cSF(3`Hly>4r)-pk)Uk&-S;fXNW-@qrUMCS z*pU|}ox!rwVpAb$Y_?y%xNG~N3n|0UplF}oll=UxmcI|A`H-~_%(G^*^$a}dBH&;T zfwApcS=2iOg+y;-W`(}$o5?ED6G!UUwJd{wU^j9gH%?y{RuX+}+vqyJxl450M)zFF zuKNWzriO4#0VY3L-hF-535B-&^OtZjlG_3k-!Ke4_ir!l<`Yl!Nd7uHDY`szZ|TW@=IHP z!O=cTzc6E#AvODA<*4>#;h9nJa6VNtS)RPTfc2yJgG5~yQ#MFSub-?sGEf6D{d&53 z){6b|xd4Z);IPN=OAz{TJ<(S&VUxBt*ewQw^AqKp!y>v*&pfm4@cll?+|6Yvfs1rxxtX0>bc5aDZs1RAMZ=r@3r@iHdBavVQk#uQ)VFUmGc%1E7PjA~c6sI)T zi-RuhZd-OCx`Q(y$kw3)R--GNcnQ|6#!@83ia~%%OO(Y$|3FIlbL(-(9d_8U#~$|) zcHBqUamRg(krHLojAT1@(iUhQKv0k5BY*Grs7IMz0|0c_0ci4ig_jm67IFgydNHNg zNl6@m{_d%Rll;*^8N;AbTGh=HVpQmA+X^`EzbWy8k8_Ddjq}&a_-4g;)z6k}m(msXUnuqs&0fVT{HA6n^#1nY z_6yj!-4b5lqEweAV2J_isV& zQ{I{z!Xtck5>pp?F@-1yMz(Bg6vuyV)aSQy&sXKQTfB&+L4-mK1gFJ(zs}efPqlr2 zA@rqU!P5sxFtfsh$Y;KPj<-VTUv~zcH4Tr@C#PESJ$vy(cOG62H5`Xa@$!89b=Y~Axg0=vI7c+G35z5%!v~C_GjQ**YF9we%4Fi z?ZuOvx0kK@{NB{;MSd^i`6ZC~J(oPm=Tc;PAx?gsOO~=)h~6wwan>$neypP1oEdF<8iva0`W co&lBDLRbH{`EJVkRr{^(e;XnH1Ct-^gol-1E&u=k