Thread Links Date Links
Thread Prev Thread Next Thread Index Date Prev Date Next Date Index

Re: [802.3_COM] local optimized phase search



Hi Adam,

George got a point, but Hossein mentioned that he saw a significant higher value of COM when changing the sample_adjustment from [-24 24] to [-32 32], which got me thinking.

May we should consider adding an "edge-detect mechanism" as a safety enhancement on top of your Local Search itick pruning? Please review the attached optimize_fom.m (rename _ItickPruningEdgeDetect.txt to .m), will need to clean it a but though.

At the present, it only evaluates iticks within +-LOCAL_SEARCH of the current best itick.
=> For example, BEST.itick= -6 and LOCAL_SEARCH= 2
=> Then only these itick are evaluated: [-8 -7 -6 -5 -4].
=> After evaluating the window, the BEST.itick= -8 and now it landed on the edge of the allowed window.
=> The FOM may still improve if we move to the left, but the optimizer does not know whether -8 is really optimal or whether -9, -10 or -11 would be even better.

The proposed edge-detect mechanism monitors whether the winning sampling phase lands on the boundary of the Local Search window. When an edge hit occurs, the search radius is automatically expanded for the next sweep, reducing the risk of missing an optimum FOM that has migrated outside the current search range.

Importantly, a single edge hit does not permanently enlarge all future searches. The expansion only applies to the subsequent sweep, after which the algorithm returns to the nominal Local Search radius unless another edge hit is detected.

I am evaluating the 684 cases to evaluate the itick pruning with and without the edge detect.

Regards,
Hansel D'SIlva
Standards Development Engineer- Amphenol


From: George Zimmerman <george@xxxxxxxxxxxxxxxxxxxx>
Sent: Tuesday, September 1, 2026 8:20 PM
To: STDS-802-3-COM@xxxxxxxxxxxxxxxxx <STDS-802-3-COM@xxxxxxxxxxxxxxxxx>
Subject: [802.3_COM] local optimized phase search

CAUTION: EXTERNAL EMAIL

This is the first time you received an email from this sender (george@xxxxxxxxxxxxxxxxxxxx). Exercise caution when clicking links, opening attachments or taking further action, before validating its authenticity.

I believe the situation being discussed on the call where the phase search appeared ill-behaved was one where the optimum sampling phase was not within the initial sweep or a valid FOM had not yet been found (e.g., because the sweep was too coarse). The local search shoudl behave similar to acquisition and tracking of sampling phase.  Once you find a valid sampling phase, you generally don't lose it except in very pathological channels (e.g., with unterminated stubs, which we generally don't see).  Such channels would be problematic anyways, as tracking loops would also get stuck in these local minima.

Note that   slide 3 of the presentation (https://www.ieee802.org/3/ad_hoc/COM/public/telecon/260901/gregory_COM_01_260901.pdf) states the requirements below and specifically warns against this.  The resolution of the initial sweep needs to be sufficient to sample the open eye (which I believe is needed for a valid FOM, if I understand the statement below correctly)
The text from slide 3 is below.  It may be useful to consider better guidance on just what "far away" is.
-george
(from slide 3)
It requires:
• Valid FOM has been found
• Local Search is enabled
• One full sweep of sampling phase has finished

If the current sample phase is far away from the best sample phase, don’t run it. Far away is defined by the Local Search parameter, which is usually set to 2


George Zimmerman, Ph.D.

President & Principal

CME Consulting, Inc.

Experts in Advanced PHYsical Communications

george@xxxxxxxxxxxxxxxxxxxx

310-920-3860

 


To unsubscribe from the STDS-802-3-COM list, click the following link: https://listserv.ieee.org/cgi-bin/wa?SUBED1=STDS-802-3-COM&A=1


To unsubscribe from the STDS-802-3-COM list, click the following link: https://listserv.ieee.org/cgi-bin/wa?SUBED1=STDS-802-3-COM&A=1

function result=optimize_fom(OP, param, chdata, sigma_bn,do_C2M)
%% License Notice
%
% Copyright 2025 802-COM Authors
% 
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
% 
% - Redistributions of source code must retain the above copyright
%   notice, this list of conditions and the following disclaimer.
% 
% - Redistributions in binary form must reproduce the above copyright
%   notice, this list of conditions and the following disclaimer in the
%   documentation and/or other materials provided with the distribution.
% 
% - Neither the name of the copyright holder nor the names of its
%   contributors may be used to endorse or promote products derived from
%   this software without specific prior written permission.
% 
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
% A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
% HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
% LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
% DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
% THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
% (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
% OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
% 
% SPDX-License-Identifier: BSD-3-Clause

%% input
% OP: struct with operational parameters
% param:  struct with configuration parameters
% chdata:  holds channel frequency responses and impulse response
% sigma_bn:  noise input used for RX_Calibration
% do_C2M: set to 0 for standard optimize_fom.  set to 1 for optimize_fom_for_C2M
%% output
% result:  struct holding all the settings associated with the best FOM (EQ, noise, etc...)

%% Initialize Loop Struct called "THIS" that will hold all settings for the current EQ loop
THIS = OptFom_Initialize_Loop_Struct();

%% Initialize parameters
f=chdata(1).faxis;

%Read user input of ts_sample_adj_range
%if one value was entered, go from 0 to that value
%if 2 values were entered, go from the 1st value to the 2nd value
if length(param.ts_sample_adj_range)==1
    param.ts_sample_adj_range(2)=param.ts_sample_adj_range(1);
    param.ts_sample_adj_range(1)=0;
end
full_sample_range=param.ts_sample_adj_range(1):param.ts_sample_adj_range(2);

% Do not allow Local Search and Box Search.  Revert TS SRCH Mode to full-sweep
if param.LOCAL_SEARCH > 0 && strcmpi(OP.TS_SRCH_MODE, 'box')
    OP.TS_SRCH_MODE = 'full-sweep';
end

% For Box Search, make the total number of sample point sweeps divisible by the box size
if strcmpi(OP.TS_SRCH_MODE, 'box')
    L = length(full_sample_range);
    required_num_points = ceil(L/OP.itick_box_size)*OP.itick_box_size;
    if L < required_num_points
        num_extra_points = required_num_points - L;
        extra_points = (1:num_extra_points)+full_sample_range(end);
        full_sample_range = [full_sample_range extra_points];
    end
end

param.ndfe_passed=param.ndfe;
% param.N_bmax is param.ndfe if groups are not used
if(param.Floating_DFE), param.ndfe=param.N_bmax; end

if OP.RxFFE && strcmp(OP.FFE_OPT_METHOD,'MMSE')
    OP.RxFFE_with_MMSE = 1;
else
    OP.RxFFE_with_MMSE = 0;
end

Gffe_values = param.cursor_gain;
if ~OP.RxFFE
    Gffe_values=0;
end

switch param.CTLE_type
    case {'CL93' 'CL120e'}
        param.g_DC_HP_values = 0;
end
lf_indx = length(param.g_DC_HP_values);

BEST.FOM = -inf;
BEST.cursor_i = [];
BEST.itick = []; % itick edge case
itick_edge_hit = false; % itick edge case

sbr = [];
if OP.DISPLAY_WINDOW
    hwaitbar=waitbar(0);
else
    fprintf('FOM search ');
end

%% T_O
if do_C2M
    loop_count=[1 2];
    T_O=floor((param.T_O/1000)*param.samples_per_ui);
    T_O=max(0,T_O);
else
    loop_count=1;
    T_O=0;
end
%% Check if the speed_up flag is set
if  OP.Optimize_loop_speed_up == 1
    OP.BinSize = 1e-4;
    OP.impulse_response_truncation_threshold = 1e-3;
end

%% Used to speed up FFE by only performing circshift when necessary
pulse_struc(1).pulse_ctle_circshift=[];
ctle_response_updated=1;

%% Build txffe values dynamically
[txffe_matrix, cur, txffe_sweep_indices, FULL_tx_index_vector, txffe_cursor_vector] = OptFom_Build_TXFFE(param);
num_txffe_runs = size(txffe_matrix,1);

% Set the final cursor_index into param struct
param.cursor_index = cur;

%% Calculate loop independent Settings:  phase_memory, qual, H_r, etc...
SETTINGS = OptFom_Calculate_Settings(txffe_matrix, chdata, param, OP);

%% if LOCAL_SEARCH> 0
if param.LOCAL_SEARCH> 0
    FOM_history = [];
    iter_count = 0;
end

%% EQ Loop
runs=length(param.ctle_gdc_values)*lf_indx*length(Gffe_values)*num_txffe_runs;
progress_interval=0.025;
pxi=0;
old_loops=0;
new_loops=0;
itick_skips=0;
itick_cases=0;
edge_hit_count=0;
FOM_TRACKER(1:length(Gffe_values),1:length(param.ctle_gdc_values),1:lf_indx,1:num_txffe_runs,1:length(full_sample_range))=0;
% turn on this debug flag "plot_iticks" to get a plot of FOM for every itick loop
plot_iticks = 0;
if plot_iticks
    figure; AxIick = axes; hold on;
end
for i=loop_count
    for Gffe_index=1:length(Gffe_values)
        param.current_ffegain=Gffe_values(Gffe_index);
        for ctle_index=1:length(param.ctle_gdc_values)
            %% CTLE Gain
            THIS.ctle_index = ctle_index;
            THIS.g_dc = param.ctle_gdc_values(ctle_index);
            CTLE_fp1 = param.CTLE_fp1(ctle_index);
            CTLE_fp2 = param.CTLE_fp2(ctle_index);
            CTLE_fz = param.CTLE_fz(ctle_index);
            % HF Boost
            ctle_gain = FD_CTLE(f, CTLE_fz, CTLE_fp1, CTLE_fp2, THIS.g_dc);
            % Mid Frequency Boost (obsolete not used)
            ctle_gain_xc = FD_CTLE(SETTINGS.f_xc, CTLE_fz, CTLE_fp1, CTLE_fp2, THIS.g_dc);
            for  g_LP_index=1:lf_indx
                %% Apply CTLE to impulse response
                THIS.g_DC_low = param.g_DC_HP_values(g_LP_index);
                THIS.g_LP_index = g_LP_index;
                
                %GDC Qual Check
                if SETTINGS.qual(g_LP_index,ctle_index)==0
                    pxi=pxi+num_txffe_runs;
                    continue;
                end
                
                % set the flag to show ctle response was updated (used to speed up TxFFE)
                if OP.INCLUDE_CTLE==1
                    ctle_response_updated=1;
                end
                
                [chdata, THIS.H_ctf, H_low_xc, H_ctf2] = OptFom_Compute_CTLE(chdata, ctle_gain, THIS, SETTINGS.f_xc, param, OP);
                
                %% Calculate noise parameters that do not depend on TxFFE, RxFFE, DFE
                Noise_XC = OptFom_Calc_Noise_XC(H_low_xc, ctle_gain_xc, SETTINGS, param, OP);
                
                % RIM 11-30-2020 moved to a subfunction
                [THIS.sigma_N] = get_sigma_eta_ACCM_noise(chdata,param,SETTINGS.H_sy,SETTINGS.H_r,THIS.H_ctf);
                if OP.RX_CALIBRATION
                    THIS.sigma_ne = get_sigma_noise( H_ctf2, param, chdata, sigma_bn); %% Equation 93A-48 %%
                    sigma_NEXT=sqrt(param.eta_0*sum( abs(SETTINGS.H_sy(2:end).^2 .* SETTINGS.H_r(2:end).*2 .* THIS.H_ctf(2:end).^2 ) .* diff(chdata(1).faxis)/1e9));
                else
                    % Equations 93A-33 and 93A-34  for NEXT - independent of TXFFE setting %%
                    % sigma_NEXT not used sigma_ne is one used in Rx calibration RIM 03-28-2024
                    % sigma_NEXT =  get_xtlk_noise( [0 1 0], 'NEXT', param, chdata );
                    THIS.sigma_ne=0;
                end
                
                %% Check GDC_MIN violation (change per 0.3k draft 2.3)
                if param.GDC_MIN ~= 0 && THIS.g_dc + THIS.g_DC_low > param.GDC_MIN
                    pxi=pxi+num_txffe_runs;
                    continue;
                end
                %% Initial PSD (without TXFFE)
                THIS.PSD_results=[];
                if OP.RxFFE_with_MMSE
                    OP.WO_TXFFE=1;
                    THIS.PSD_results=get_PSDs(THIS.PSD_results,[],[],[],THIS.g_dc,THIS.g_DC_low,param,chdata,OP);
                end
                %% TXFFE Loop
                %Originally this was a separate for loop for each tap, but it is now all contained in the txffe_matrix to use a single modular loop
                for TK=1:num_txffe_runs
                    pxi=pxi+1;
                    progress = pxi/runs;
                    if OP.DISPLAY_WINDOW
                        if ~mod(pxi,floor(runs*progress_interval))
                            waitbar(progress, hwaitbar, 'Linear equalization tuning'); figure(hwaitbar); drawnow;
                        end
                    else
                        if ~mod(pxi,floor(runs*progress_interval)), fprintf('%i%% ', round(progress*100) );end
                    end
                    
                    % Skip combinations with small values of c(0), not guaranteed to be supported by all transmitters.
                    txffe_cur=txffe_cursor_vector(TK);
                    if txffe_cur<param.tx_ffe_c0_min
                        continue;
                    end
                    
                    %get the index used for each tap on this iteration (needed for LOCAL SEARCH)
                    THIS.tx_index_vector=FULL_tx_index_vector(TK,:);
                    
                    %% LOCAL SEARCH
                    old_loops=old_loops+1;
                    if param.LOCAL_SEARCH>0 && ~isinf(BEST.FOM)
                        iter_count= iter_count + 1;
						
                        if param.NonZeroLSMethod== 1
                            skip_it = OptFom_Adaptive_Local_Search(param.LOCAL_SEARCH, param.Overwrite_Min_Radius, BEST, THIS, FOM_history, iter_count, num_txffe_runs);
                        else
                            skip_it = OptFom_Local_Search(param.LOCAL_SEARCH, BEST, THIS, txffe_sweep_indices);
                        end
                        
                        if skip_it
                            continue;
                        end
                    end
                    new_loops=new_loops+1;
                    
                    %% TXFFE
                    %fetch txffe for this iteration
                    THIS.txffe=txffe_matrix(TK,:);
                    [sbr, chdata, pulse_struc] = OptFom_Compute_TXFFE(chdata, pulse_struc, THIS.txffe, ctle_response_updated, param, OP);
                    % IMPORTANT:  remember sbr just after txffe so it can be restored on each itick loop
                    sbr_from_txffe=sbr;
                    % after txffe, ctle updated flag is off until the next CTLE loop
                    if ctle_response_updated
                        ctle_response_updated = 0;
                    end
                    
                    %% Find Sample Location
                    [raw_cursor_i, no_zero_crossing, sbr_peak_i] = OptFom_Find_Sample_Point(sbr, param, OP, SETTINGS.Peak_Search_Range);
                    if no_zero_crossing
                        continue;
                    end
                    triple_transit_time = round(sbr_peak_i*2/param.samples_per_ui)+20;
                    if SETTINGS.min_number_of_UI_in_response < triple_transit_time
                        SETTINGS.min_number_of_UI_in_response = triple_transit_time;
                    end
                    
                    %% ITICK LOOP
                    [loop_range, BEST, box_search, cluster, box_mid] = OptFom_Setup_Sampler_Sweep(full_sample_range, BEST, OP);

                    effective_radius = param.LOCAL_SEARCH;

                    if itick_edge_hit
                        effective_radius = 2*param.LOCAL_SEARCH;
                    end

                    % reset; this sweep will decide the radius of NEXT sweep
                    itick_edge_hit = false;

                  
                    % itick edge case
                    itick_search_center = [];
                    if ~isinf(BEST.FOM) && ...
                            param.LOCAL_SEARCH>0 && ...
                            ~box_search

                        itick_search_center = BEST.itick;

                    end


                    for itickn=loop_range

                        %% get cursor index for this itick loop
                        if box_search
                            [THIS.itick, BEST, skip_it] = OptFom_Itick_BoxSearch(itickn, cluster, BEST, box_mid, OP.itick_box_size);
                            if skip_it
                                continue;
                            end
                        else
                            THIS.itick=full_sample_range(itickn);
                        end
                        THIS.cursor_i = raw_cursor_i+THIS.itick;
                        
                        % IMPORTANT:  restore sbr on each loop (can't use the one with RxFFE applied)
                        sbr=sbr_from_txffe;
                        
                        %% Local Search for +/- itick sweep
                        % itick_cases=itick_cases+1;
                        % if ~isinf(BEST.FOM) && param.LOCAL_SEARCH > 0 && ~box_search && max(THIS.g_LP_index, THIS.ctle_index) > 1
                        %     if abs(THIS.itick-BEST.itick) > param.LOCAL_SEARCH
                        %         itick_skips = itick_skips+1;
                        %         continue;
                        %     end
                        % end

                        itick_cases=itick_cases+1;

                        % effective_radius = param.LOCAL_SEARCH;
                        % 
                        % % Expand search window if previous search hit boundary
                        % if itick_edge_hit
                        %     effective_radius = 2 * param.LOCAL_SEARCH;
                        % end
                        
                        if ~isinf(BEST.FOM) && ...
                                param.LOCAL_SEARCH > 0 &&...
                                ~box_search &&...
                                max([THIS.g_LP_index THIS.ctle_index TK]) > 1

                            if abs(THIS.itick - BEST.itick) > effective_radius %param.LOCAL_SEARCH
                                itick_skips = itick_skips+1;
                                continue;
                            end

                        end


                        %% RXFFE: updates C, floating_tap_locations, FOM, PSD_results, and MMSE_results
                        if OP.RxFFE
                            [sbr, THIS, skip_it] = OptFom_Compute_RxFFE(sbr, THIS, Noise_XC, chdata, param, OP);
                            if skip_it
                                continue;
                            end
                        end
                        
                        %% 93A.1.6 step c defines A_s %%
                        cursor = sbr(THIS.cursor_i);
                        THIS.A_p=sbr(sbr_peak_i);
                        THIS.A_s = param.R_LM*cursor/(param.levels-1);
                        if isempty(SETTINGS.delta_sbr)
                            SETTINGS.delta_sbr = sbr;
                        end
                        sbr=sbr(:);
                        
                        %% Equation 93A-27: "otherwise" case
                        THIS.far_cursors = sbr(THIS.cursor_i-T_O+param.samples_per_ui*(param.ndfe+1):param.samples_per_ui:end);
                        t=((THIS.cursor_i+param.samples_per_ui*(param.ndfe+1):param.samples_per_ui:length(sbr))-(THIS.cursor_i+param.samples_per_ui*(param.ndfe+1)))*...
                            param.ui/param.samples_per_ui;
                        THIS.precursors = sbr(THIS.cursor_i-param.samples_per_ui:-param.samples_per_ui:1);
                        THIS.precursors = THIS.precursors(end:-1:1);
                        
                        %% skip this case if FOM has no chance of beating old FOM
                        %this is also done below but with excess_dfe_cursors included.
                        %excess_dfe_cursors requires the floating DFE computation which is
                        %time consuming, so checking here can have significant run time improvements
                        if ~OP.RxFFE_with_MMSE
                            sigma_ISI_ignoreDFE = param.sigma_X*norm([THIS.precursors;  THIS.far_cursors]);
                            if (20*log10(THIS.A_s/sigma_ISI_ignoreDFE) < BEST.FOM)
                                continue
                            end
                        end
                        
                        %% sbr required length = cursor + all DFE UI + 1 additional UI
                        sbr_required_length=THIS.cursor_i+param.samples_per_ui*(param.ndfe+1);
                        if length(sbr)<sbr_required_length
                            sbr(end+1:sbr_required_length)=0;
                        end
                        
                        %% Solve DFE: updates dfetaps, floating_tap_coef, tail_RSS, excess_dfe_cursors, and floating_tap_locations
                        [THIS, param] = OptFom_Compute_DFE(sbr, THIS, param, do_C2M, T_O);
                        
                        %% Calculate all Noise: updates h_J, sigma_TX, ISI_N, sigma_N, total_noise_rms
                        [THIS, abort_status]  = OptFom_Calc_Noise(THIS, BEST.FOM, sbr, SETTINGS, chdata, param, OP);
                        if abort_status == 1
                            continue;
                        elseif abort_status == 2
                            break;
                        end
                        
                        %% Find FOM (unless using RxFFE with MMSE since FOM has already been found)
                        if ~OP.RxFFE_with_MMSE
                            [THIS.FOM, skip_loop] = OptFom_Calc_FOM(chdata, do_C2M, THIS, param, OP, sbr);
                            if skip_loop
                                continue;
                            end
                        end

                        if param.LOCAL_SEARCH>0
                            % Update FOM history for adaptive local search
                            FOM_history = [FOM_history THIS.FOM];
                            max_history_length = length(loop_range);
                            if length(FOM_history) > max_history_length
                                FOM_history = FOM_history(end-max_history_length+1:end);
                            end
                        end
                        
                        itick_index=find(THIS.itick==full_sample_range);
                        FOM_TRACKER(Gffe_index,ctle_index,g_LP_index,TK,itick_index)=THIS.FOM;
                        
                        %% Update Best Settings
                        BEST = OptFom_Set_Best_Itick(THIS, BEST);

                        %% Detect if optimum landed on search boundary

                        if ~isempty(itick_search_center)
                            if abs(BEST.itick - itick_search_center) == effective_radius %>= effective_radius

                                itick_edge_hit= true;

                                edge_hit_count= edge_hit_count + 1;

                                fprintf(['EDGE HIT: center=%d best=%d ' ...
                                    'radius=%d CTLE=%d LP=%d TX=%d\n'], ...
                                    itick_search_center, ...
                                    BEST.itick, ...
                                    effective_radius, ...
                                    THIS.ctle_index, ...
                                    THIS.g_LP_index, ...
                                    TK);
                            % else
                            % 
                            %     itick_edge_hit = false;



                            end

                        end


                        if (THIS.FOM > BEST.FOM)
                            BEST = OptFom_Update_Best_Setttings(BEST, THIS, sbr, chdata, param, OP);
                        end
                    end
                    if plot_iticks
                        plot(AxIick, full_sample_range,squeeze(FOM_TRACKER(Gffe_index,ctle_index,g_LP_index,TK,:)));
                    end
                end
                
            end
        end
    end
    if do_C2M
        if  BEST.FOM == -inf
            param.Min_VEO_Test=0;
        else
            break
        end
    end
end
%%
if 0
    fprintf('old loops = %d\n',old_loops);
    fprintf('new loops = %d\n',new_loops);
    display(sprintf('\n :loops = %g',pxi))
end

%turn this on to review if FOM changes sign more than once in an itick loop
if 0
    DIR_CHANGE={};
    for m=1:length(Gffe_values)
        for n=1:length(gdc_values)
            for k=1:lf_indx
                FOM_this_mat=squeeze(FOM_TRACKER(m,n,k,:,:));
                %x reveals if FOM on a particular row (locked txffe, moving itick) goes up or down
                %1 = goes up, -1=goes down
                x=sign(diff(FOM_this_mat')');
                %y = change in sign on x.  the location of a "2" is where FOM changes direction
                y=abs(diff(x'))';
                %the goal is the FOM only changes direction once. so count the occurences of the 2
                for j=1:size(FOM_this_mat,1)
                    z{j}=find(y(j,:)==2);
                end
                zL=cellfun('length',z);
                %return any row where FOM changed direction more than once
                DIR_CHANGE{j,k}=find(zL>1);
            end
        end
    end
    multi_direction_change=find(~cellfun('isempty',DIR_CHANGE))
end

if 1
	fprintf('\n');
    fprintf('Itick cases = %d\n', itick_cases);
    fprintf('Itick skips = %d\n', itick_skips);

    if itick_cases > 0
        fprintf('Itick skip rate = %.1f %%\n', ...
            100*itick_skips/itick_cases);
    end

    fprintf('Edge hits = %d (%.2f%% of itick cases)\n', ...
        edge_hit_count, ...
        100*edge_hit_count/max(1,itick_cases));
end

%% Check if EQ failed (take last setting)
if isempty(BEST.cursor_i)
    result.eq_failed=true;
    fprintf('equalization failed\n');
    BEST = OptFom_Update_Best_Settings_EQ_Failed(BEST, THIS, sbr, chdata, param, OP);
    if do_C2M
        return
    end
else
    result.eq_failed=false; % RIM 12/30/2023
end

f=1e8:1e8:100e9;
%use length of BEST.sbr for time axis in case zero padding was performed
length_sbr=length(BEST.sbr);
t=0:param.ui/param.samples_per_ui:(length_sbr-1)*param.ui/param.samples_per_ui;

%% Update fields in BEST that are only needed after EQ optimization concludes
BEST = OptFom_Update_BEST_Post_Optimize(BEST, f, param, OP);

%% Plot Results
OptFom_Plot_Best_Results(BEST, t, f, chdata, param, OP);

if OP.DISPLAY_WINDOW
    close(hwaitbar);
else
    fprintf('\n');
end

%% Create final output structure
result = OptFom_Create_Output(result, BEST, t, chdata, param, OP);

________________________________________________________________________
To unsubscribe from the STDS-802-3-COM list, click the following link: https://listserv.ieee.org/cgi-bin/wa?SUBED1=STDS-802-3-COM&A=1