
clear;
col = linspecer(6);

% GENERATE SIR CURVE
if ismac
    foldername = '/Users/silasoettinghaus/Documents/MATLAB/Labor_Datensatz_PAM4_MPI/pam4_10km_1km';
else
    foldername = 'C:\Users\Silas\Documents\MATLAB\Datensätze\Labor_Datensatz_PAM4_MPI_OFC2023\pam4_10km_1km';
end


% SETTINGS

optimize_mudc = 0;
run_sir_sweep = 0;
run_feed_forward = 1;
run_baseline  = 0;
run_ideal_dc_tap = 0;
plot_timesignal = 1;

block_loop = [1];

for bl = 1:numel(block_loop)

    eq_parallelization_blocklength = block_loop(bl);
    eq_updatelatency = 3;
    eq_avg_blocklength =0;
    
    % FIND BEST MU DC
    
    if optimize_mudc
        mudc_loop = [0, 0.0001,0.0005, 0.001,0.005, 0.01:0.01:0.1, 0.2:0.1:1];
        
        current_filename = 'pam4__loop_14_92Gbd_19092023_1513.mat';
        current_filename = 'pam4__loop_29_56Gbd_19092023_1546.mat';
        recorded_data = load([foldername,filesep, current_filename]);
    
        [best_mudc,ber]= optimizeMuDc(recorded_data,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc_loop);
    
%         figure(16)
%         hold on
%         plot(mudc_loop,ber);
%         set(gca, 'YScale', 'log');
%         set(gca, 'XScale', 'log');
%         scatter(best_mudc,ber(mudc_loop==best_mudc),100,'Marker','x','LineWidth',2)
%         yline(ber(1));
%         xlim([0,1])
    end
    
    if run_sir_sweep
    
        mudc = best_mudc;
        [ber,sir,fsym] = runSIRsweep(eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc);

        plotSirSweep(ber,sir,fsym,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc)

        crossing = calculateCrossing([92e9, 56e9],ber,sir,fsym);
    
        req_sir_56(bl) = crossing(2,2) ;
        req_sir_92(bl) = crossing(2,1) ;

    end


end

if run_feed_forward

        current_filename = 'pam4__loop_14_92Gbd_v19092023_1513.mat';
        recorded_data = load([foldername,filesep, current_filename]);
        eq_avg_blocklength = [50,100,1000,3000];
        [best_block,ber]= optimizeAvgBlocklength(recorded_data,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,0);


        best_block = 400;

        [ber_base,sir_base,fsym_base] = runSIRsweep(eq_parallelization_blocklength,eq_updatelatency,best_block,0);

        plotSirSweep(ber_base,sir_base,fsym_base,eq_parallelization_blocklength,eq_updatelatency,best_block,0)

        crossing = calculateCrossing([92e9, 56e9],ber_base,sir_base,fsym_base);

        [ber_base,sir_base,fsym_base] = runSIRsweep(eq_parallelization_blocklength,eq_updatelatency,0,0);

        plotSirSweep(ber_base,sir_base,fsym_base,eq_parallelization_blocklength,eq_updatelatency,0,0)

        crossing = calculateCrossing([92e9, 56e9],ber_base,sir_base,fsym_base);

end


if run_baseline

    mudc = 0;
    [ber_base,sir_base,fsym_base] = runSIRsweep(eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc);

    plotSirSweep(ber_base,sir_base,fsym_base,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc)

    crossing = calculateCrossing([92e9, 56e9],ber_base,sir_base,fsym_base);

    req_sir_56_baseline = crossing(2,2) ;
    req_sir_92_baseline = crossing(2,1) ;

end

if run_ideal_dc_tap

    current_filename = 'pam4__loop_29_56Gbd_19092023_1546.mat';
    recorded_data = load([foldername,filesep, current_filename]);
    [mudc,~]= optimizeMuDc(recorded_data,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc_loop);

    eq_parallelization_blocklength = 1;
    eq_updatelatency = 1;
    [ber_ideal,sir_ideal,fsym_ideal] = runSIRsweep(eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc);

    plotSirSweep(ber_ideal,sir_ideal,fsym_ideal,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc)

    crossing = calculateCrossing([92e9, 56e9],ber_ideal,sir_ideal,fsym_ideal);

    req_sir_56_ideal = crossing(2,2) ;
    req_sir_92_ideal = crossing(2,1) ;

end



if plot_timesignal
    
    current_filename = 'pam4__loop_26_92Gbd_19092023_1542.mat';
    recorded_data = load([foldername,filesep, current_filename]);

    sir = recorded_data.saveStructTemp.awg2scope_keysight_state.eigenlight_mpi - 3.3 + 7.2;

    fsym = 92e9;
    eq_parallelization_blocklength = 1;
    eq_updatelatency = 1;
    eq_avg_blocklength = 0;

    % TRUE SYMBOLS
    correct_symbols     = recorded_data.saveStructTemp.digi_mod_out';

        %3) PLAIN RECEIVED SIGNAL
    disp('RX Signal')
    y_rx = Electricalsignal(recorded_data.saveStructTemp.dp_tsynch_out');
    y_rx.fs = 2.*fsym;
    rx_symbols          = y_rx.resample("fs_in",2.*fsym,"fs_out",fsym);
    rx_symbols          = rx_symbols.normalize("mode","rms").signal;
    disp(std(rx_symbols));
    scatterleveldependent(rx_symbols,correct_symbols,fsym);

    %1) PLOT WITH WITH ALGORITHM
    if 0
    mudc_loop = [0, 0.0001,0.0005, 0.001,0.005, 0.01:0.01:0.1, 0.2:0.1:1];
    [mudc,~]= optimizeMuDc(recorded_data,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc_loop);
    end 

    disp('ALGORITHM ')
    mudc = 0.05;
    results_alg = runPostProcessing(recorded_data,mudc,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength);
    disp(results_alg.ber);
    rx_symbols = results_alg.EQ_out.signal; 
    disp(std(rx_symbols));
    scatterleveldependent(rx_symbols,correct_symbols,fsym);

    %2) JUST EQ
    disp('Just EQ')
    mudc = 0;
    results = runPostProcessing(recorded_data,mudc,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength);
    disp(results.ber)
    rx_symbols = results.EQ_out.signal; 
    disp(std(rx_symbols));
    scatterleveldependent(rx_symbols,correct_symbols,fsym);
    



end


function plotSirSweep(ber,sir,fsym,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc)


        rate = [92e9, 56e9];
        figure(95)

        for r = 1:length(rate)

            sorted = sortrows([sir(fsym == rate(r)); ber(fsym == rate(r))]',1)';
            hold on
            plot(abs(sorted(1,:)),sorted(2,:),'DisplayName',[num2str(rate(r).*1e-9),' GBd; PAM4; mudc: ',num2str(mudc)],'LineWidth',1,'Marker','o','MarkerSize',5,'LineStyle','-','HandleVisibility','on');

        end

        set(gca, 'YScale', 'log');
        yline(3.8e-3,'LineWidth',1, 'LineStyle','--','HandleVisibility','off');
        xlim([15,35]);
        xlabel('SIR in dB');
        ylabel('BER');

end

function [crossing] = calculateCrossing(rate,ber,sir,fsym)

    for r = 1:length(rate)
        sorted = sortrows([sir(fsym == rate(r)); ber(fsym == rate(r))]',1)';
        berVals = sorted(2,:);
        xAxis = sorted(1,:);
        
        hdfec = 3.8e-3 .* ones(1,length(sorted));
        crossing(:,r) = InterX([hdfec(:)';xAxis],[berVals;xAxis]) ; 
    end

end

function [ber,sir,fsym] = runSIRsweep(eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc)

    % GENERATE SIR CURVE
    if ismac
        foldername = '/Users/silasoettinghaus/Documents/MATLAB/Labor_Datensatz_PAM4_MPI/pam4_10km_1km';
    else
        foldername = 'C:\Users\Silas\Documents\MATLAB\Labor_Datensatz_PAM4_MPI\pam4_10km_1km';
    end
    
    allfiles = dir(foldername);
    
    parfor i = 1:length(allfiles)
    
        if allfiles(i).bytes ~= 0
            current_filename = allfiles(i).name;
            recorded_data = load([foldername,filesep, current_filename]);
        else
            continue
        end
   
        results = runPostProcessing(recorded_data,mudc,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength);
        ber(i) = results.ber;
        fsym(i) = results.fsym;
        sir(i) = results.sir
    
        disp(['fsym: ',num2str(results.fsym*1e-9),'GBd, SIR: ',num2str(results.sir),' ->> BER: ',sprintf('%2E',results.ber)]);
    
    end
end

function [best_blocklength,ber] = optimizeAvgBlocklength(recorded_data,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength_loop,mudc)

    for j = 1:length(eq_avg_blocklength_loop)    
        eq_avg_blocklength = eq_avg_blocklength_loop(j);

    
        results = runPostProcessing(recorded_data,mudc,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength);
        ber(j) = results.ber;
        
        disp(['fsym: ',num2str(results.fsym*1e-9),'GBd, SIR: ',num2str(results.sir),' ->> BER: ',sprintf('%2E',results.ber)]);
    
    end
    
    [~,pos]=min(ber);
    best_blocklength = eq_avg_blocklength_loop(pos);

end

function [best_mudc,ber] = optimizeMuDc(recorded_data,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength,mudc_loop)

    parfor j = 1:length(mudc_loop)    
        mudc = mudc_loop(j);

    
        results = runPostProcessing(recorded_data,mudc,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength);
        ber(j) = results.ber;
        
        disp(['fsym: ',num2str(results.fsym*1e-9),'GBd, SIR: ',num2str(results.sir),' ->> BER: ',sprintf('%2E',results.ber)]);
    
    end
    
    [~,pos]=min(ber);
    best_mudc = mudc_loop(pos);

end

function results = runPostProcessing(recorded_data,mudc,eq_parallelization_blocklength,eq_updatelatency,eq_avg_blocklength)

        results.sir  = recorded_data.saveStructTemp.awg2scope_keysight_state.eigenlight_mpi - 3.3 + 7.2;
        results.fsym = recorded_data.saveStructTemp.common.f_sym;

        results.fdac = recorded_data.saveStructTemp.common.f_DAC;
        results.fadc = recorded_data.saveStructTemp.common.f_ADC;


        % 0) build RX Signal
        y_rx        = Electricalsignal(recorded_data.saveStructTemp.dp_tsynch_out');
        y_rx.fs     = 2.*results.fsym;

        % 0) build tx reference Signal for eq training
        y_digimod   = Electricalsignal(recorded_data.saveStructTemp.digi_mod_out');
        y_digimod.fs = results.fsym;


        % 1) normlaize
        y_rx = y_rx.normalize("mode","rms");

        eq = EQ_silas("Ne",[25,0,0],"Nb",[2,0,0],"trainlength",4096,...
            "sps",2,...
            "mu_dc_dd",mudc,...
            "mu_dc_train",mudc,...
            "mu_ffe_train",0,...
            "mu_dfe_train",0.005,...
            "mu_ffe_dd",[0.0004 0.0004 0.0004],...
            "mu_dfe_dd",0.005,...
            "ddloops",3,...
            "trainloops",4,...
            "eq_parallelization_blocklength",eq_parallelization_blocklength, ...
            "eq_updatelatency",eq_updatelatency,...
            "eq_avg_blocklength",eq_avg_blocklength);

        [Eq_out] = eq.process(y_rx,y_digimod);

        results.EQ_out = Eq_out;

        % 3) digital demodulation object
        digimod = PAMmapper(2^recorded_data.saveStructTemp.common.M,0);
        
        %estimated/ equalized symbol sequence
        d_estimated = digimod.demap(Eq_out);

        %correct data symbols
        d_correct = recorded_data.saveStructTemp.prms_out;

        % 4) BER calculation
        [totalbits,errors,results.ber,loc]     = calc_ber(d_estimated.signal(1:end,:)    ,d_correct(1:end,:)',"skip",0,"returnErrorLocation",1);
end

function std_per_lvl = calcleveldependentstd(rx_symbols,correct_symbols)
        error_of_rx_signal =  rx_symbols - correct_symbols;
        levels = unique(correct_symbols);
        for l = 1:4
            level_amplitude = levels(l);
            std_per_lvl(l) = var(( 1/64 .* movsum(error_of_rx_signal(correct_symbols==level_amplitude),[64/2,64/2])  ));
            %std_per_lvl(l) = std(error_of_rx_signal(correct_symbols==level_amplitude));
        end
end

function scatterleveldependent(rx_symbols,correct_symbols,f_sym)
        col = cbrewer2('Paired',8);
        ccnt = -1;
        figure1 = figure();
        levels = unique(correct_symbols);

        start = 1;
        ende = length(correct_symbols);

        start = 30000;
        ende = 40000;
        
        for l = 1:4
            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');
            xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6;
            xax_in_sec = 1:length(correct_symbols);

            scatter(xax_in_sec(start:ende),symbols_for_lvl(start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:));
            hold on;
        end

        std_lvl = round(std_lvl,2);
        disp(std_lvl);

        ccnt = 0;

        % Add the windowed/ smoothed curves
        for l = 1:4
            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]);

            symbols_for_lvl(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));

            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,:));
            hold on
        end

        %yline(max(rx_symbols(correct_symbols==levels(2))))

        if 0
        annotation(figure1,'textbox',...
            [0.660523809523809 0.844444444444448 0.133523809523809 0.0603174603174607],...
            'String',['\sigma = ',num2str(std_lvl(4))],...
            'LineWidth',1.8,...
            'LineStyle','none',...
            'FontSize',12,...
            'FitBoxToText','off');
        
        % Create textbox
        annotation(figure1,'textbox',...
            [0.667666666666665 0.642857142857147 0.133523809523809 0.0603174603174607],...
            'String',['\sigma = ',num2str(std_lvl(3))],...
            'LineWidth',1.8,...
            'LineStyle','none',...
            'FontSize',12,...
            'FitBoxToText','off');
        
        % Create textbox
        annotation(figure1,'textbox',...
            [0.671238095238093 0.442857142857148 0.133523809523809 0.0603174603174608],...
            'String',['\sigma = ',num2str(std_lvl(2))],...
            'LineWidth',1.8,...
            'LineStyle','none',...
            'FontSize',12,...
            'FitBoxToText','off');
        
        % Create textbox
        annotation(figure1,'textbox',...
            [0.670047619047616 0.265079365079371 0.133523809523809 0.0603174603174608],...
            'String',['\sigma = ',num2str(std_lvl(1))],...
            'LineWidth',1.8,...
            'LineStyle','none',...
            'FontSize',12,...
            'FitBoxToText','off');
        end

        xlim([0, 2.6])
        ylim([-2 2])
        xlabel('Time in $\mu$s');
        ylabel('Normalized Amplitude');

end


