%% Settings

clear
for M=[4]


    filename = '112G_2';
    load_sequence = 0;

    datarate = 448e9;

    kover = 8;
    fsym = round(datarate*1e-9 / log2(M))*1e9;
    fdac = fsym;%256e9;
    fadc = 256e9;

    lowpass_cutoff = fsym/2 * 1.1;
    awg_bw = lowpass_cutoff;
    mod_bw = lowpass_cutoff;
    phd_bw = lowpass_cutoff;
    scp_bw = lowpass_cutoff;

    LP_awg      = Filter('filtdegree',4,"f_cutoff",75e9,"fs",fdac*kover,"filterType",filtertypes.butterworth);
    LP_modulator= Filter('filtdegree',2,"f_cutoff",70e9,"fs",fdac*kover,"filterType",filtertypes.butterworth);
    LP_opt      = Filter('filtdegree',3,"f_cutoff",fsym/log2(M).*1.5,"fs",fdac*kover,"filterType",filtertypes.gaussian);
    LP_phd      = Filter('filtdegree',2,"f_cutoff",70e9,"fs",fdac*kover,"filterType",filtertypes.butterworth);
    LP_scpe     = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth);

    % 1) PRBS Generation
    O = 18; %order of prbs
    N = 2^(O-1); %length of prbs
    [~,seed] = prbs(O,1); %initialize first seed of prbs
    bitpattern=[];

    for i = 1:log2(M)
        [bitpattern(:,i),seed] = prbs(O,N,seed);
    end

    if M == 6
        bitpattern = reshape(bitpattern,[],1);
        bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
    end

    % 2 ) Build Inf. signal class
    bits = Informationsignal(bitpattern);

    % 3) Digi modulation -> PAM-M signal
    digimod_out = PAMmapper(M,0).map(bits);
    digimod_out.fs = fsym;


    % linearGain = 1;
    % limit = 1;
    % SaturatingAmplifier = serdes.SaturatingAmplifier('Mode',1,...
    %           'Limit',limit,'LinearGain',linearGain);
    % X.signal = SaturatingAmplifier(X.signal);
    % X.signal = min(max(X.signal,-0.8),0.8);
    % X = X.normalize("mode","oneone");

    sir = [20:2:36];  %decibel = attenuation of interference path
    laser_linewidth = [1e5 1e6 10e6];
    pn_key = [1:10];
    vp = [0.25,0.5,0.75,1];
    vb = [1:0.1:1.8];

    rop = 0;
    sir = 28;
    laser_linewidth = 10e6;
    pn_key = 9;
    vp = 0.5;
    vb = 1;%[1:0.1:1.8];
    mpi_path = 50;

    cnt = 1;

    for s = 1:length(sir)
        for l = 1:length(laser_linewidth)
            for pnk = 1:length(pn_key)
                for n = 1:length(vp)
                    for m = 1:length(vb)

                        %digimod_out = digimod_out.normalize("mode","oneone");

                        % cnt = cnt+1;
                        %X = Pulseformer("fsym",fsym,"fdac",fdac,"pulse","rrc","pulselength",16,"rrcalpha",0.05).process(digimod_out);
                        X = digimod_out;

%                         % precomp 
                        [b,a] = butter(2,75e9/(fsym/2),"low");
                        H=freqz(b,a,length(X),fsym,'whole');
                        max_amp_db = 3;
                        p = find(abs(H)<10^(-max_amp_db/20));
                        H(p) = 10^(-max_amp_db/20);
                        H_maxatt = H;
                        H_maxatt = max(H,10^(-20/20));
                        H_inv = 1./H_maxatt;
                        
                        freq_vec = linspace(-X.fs/2,X.fs/2,length(X));

                        h = sin(pi*freq_vec/fdac)./(pi*freq_vec/fdac);
                        
                        max_amp_db = 40;
                        max_amp_lin = 10^(-max_amp_db/20);

                        p = find(h<max_amp_lin);

                        h(p)=10^(-max_amp_db/20);

                        h = 1./h;

                        figure(13);
                        plot(freq_vec.*1e-9,20*log10(fftshift(H_inv)));
                        hold on
                        plot(freq_vec.*1e-9,20*log10((h)));
                        

                         
                         X.signal = ifft(fftshift(h).'.*fft(X.signal));
%                         X.signal = ifft(H_inv.*fft(X.signal));

                        figure(12)
                        spectrum_plot(X.signal,fsym);                 
  
                        % 5) AWG (lowpass, quantization, sample and hold)
                        X = AWG("fdac",fdac,"dac_min",-1,"dac_max",1,"lpf_active",0,"H_lpf",LP_awg,"kover",kover,"bit_resolution",16,"normalize2dac",1,"upsampling_method","samplehold").process(X);
                            
                        spectrum_plot(X.signal,X.fs);
                           

                        burg_coeff = arburg(X.signal(10000:20000),100);

                            [h,w] = freqz(1,burg_coeff,length(X),"whole",fsym*kover);
                            h = h/max(abs(h));
                            hold on
                            plot(w,20*log10(h),'DisplayName','Burg Coeff');

                        spectrum_plot(X.signal,X.fs);

                        % 6) Lowpass behavior before laser
                        X =  LP_modulator.process(X);

                        % 7) Normalize signal
                        X = X.normalize("mode","oneone");

                        % 1) Laser; Modulation ->  OPTICAL DOMAIN
                        u_pi = 2;
                        vbias = -vb(m);
                        extmodlaser = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",X.fs,"lambda",1290,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth(l),"randomkey",pn_key(pnk));
                        E = X.*vp(n);

                        [Opt,extmodlaser] = extmodlaser.process(E);
                        
                        figure(m)
                        hold on
                        scatter(E.signal(1:100000),(abs(Opt.signal(1:100000)).^2)*1e3,0.1,'.','DisplayName','Modulator TF')
                        xlabel('Input in V')
                        ylabel('abs(Output) in mW')

                        % ER = 10*log10(max(abs(Opt.signal).^2)/min(abs(Opt.signal).^2));

                        Opt = LP_opt.process(Opt);

                        cspr(s,l,pnk,n,m) = Opt.cspr;
                        mod_out_pow(s,l,pnk,n,m) = Opt.power;

                        % 2) ping pong fiber propagation
                        Interference_sig    = Fiber("fsimu",Opt.fs,"fiber_length",mpi_path*2/1000,"alpha",0,"D",0,"lambda0",1310,"gamma",0).process(Opt);

                        Interference_sig    = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",-sir(s)).process(Interference_sig);

                        % In the meantime: delay the main signal
                        [Main_sig,dly] = Opt.delay("delay_meter",mpi_path*2);

                        % Add
                        Combined_sig = Main_sig + Interference_sig;

                        % Cut (due to the delays there is a jump in the signals)
                        if dly == 0;dly = 1;end
                        Combined_sig.signal = Combined_sig.signal(ceil(dly):end);

                        % Fiber
                        Combined_sig    = Fiber("fsimu",Combined_sig.fs,"fiber_length",2,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.08).process(Combined_sig);

                        for i = 1:length(rop)

                            % Set ROP
                            Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",rop(i)).process(Combined_sig);
                            rop_save(s,l,pnk,n,m,i) = Rx_sig.power;

                            % Square Law
                            Rx_sig = Photodiode("fsimu",Rx_sig.fs,"dark_current",2e-08,"responsivity",1,"temperature",20).process(Rx_sig);

                            %Lowpass PhDiode
                            Rx_sig = LP_phd.process(Rx_sig);

                            % Scope
                            Scpe_sig = Scope("fsimu",Rx_sig.fs,"fadc",fadc,...
                                "delay",0,"fixed_delay",0,"lpf_bw",scp_bw,"filtertype",filtertypes.butterworth,...
                                "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0,...
                                "adcresolution",16,"quantbuffer",0.1,'block_dc',1,'lpf_active',1,'H_lpf',LP_scpe).process(Rx_sig);

                            % Sample to 2x fsym
                            Scpe_sig =  Scpe_sig.resample("fs_in",fadc,"fs_out",2*fsym);

                            % Sync Rx signal with reference
                            [Scpe_sig,D,cuts] = Scpe_sig.tsynch("reference",digimod_out,"fs_ref",fsym);

                            %                                         %             % simple EQ (optimum mudc: 0.05 -> 0.005)
                            %                                         EQ_sig = EQ_silas_plain("Ne",[20,8,8],"Nb",[2,0,0],"trainlength",4096,"mu_dc_dd",0.005,"mu_dc_train",0.05,...
                            %                                             "mu_ffe_train",0.005,"mu_combined_dd",[0.0004 0.0006 0.0003 0.005],"ddloops",3,'trainloops',3,'sps',2).process(Rx_sig,digimod_out);

%                             EQ_sig = EQ("K",2,"plottrain",0,"plotfinal",0,...
%                                 "training_length",4096,"training_loops",3,...
%                                 "Ne",[50,8,8],"Nb",[2,0,0],...
%                                 "DCmu",0.005,"DDmu",[0.0004 0.0006 0.0003 0.005],"DFEmu",0.005,"FFEmu",0.00,...
%                                 "dd_loops",3,"epsilon",[10 100 1000 ],"M",2,...
%                                 "thres",[0.005 0.004 0.0005 ],"l1act",0,"delay",0,"rho",0.0005,"ideal_dfe",0,"DB_aim",0).process(Scpe_sig,digimod_out);

                            [EQ_sig,EQ_sym] = EQ_silas("Ne",[50,8,8],"Nb",[2,0,0],"trainlength",4096,...
                                    "sps",2,...
                                    "mu_dc_dd",0.00,...
                                    "mu_dc_train",0.0,...
                                    "mu_ffe_train",0.00,...
                                    "mu_dfe_train",0.005,...
                                    "mu_ffe_dd",[0.0004 0.0006 0.0003],...
                                    "mu_dfe_dd",0.005,...
                                    "ddloops",3,...
                                    "trainloops",3,...
                                    "eq_parallelization_blocklength",1, ...
                                    "eq_updatelatency",1,...
                                    "eq_avg_blocklength",0).process(Scpe_sig,digimod_out);

                            % Demap

                            Rx_Bits = PAMmapper(M,0).demap(EQ_sym);

                            % BER
                            [~,errors_bm,BER(s,l,pnk,n,m,i),errors] = calc_ber(Rx_Bits.signal,bitpattern,"skip_front",100,"skip_end",150,"returnErrorLocation",1);

                            formatted_ber = sprintf('%.1e', BER(s,l,pnk,n,m,i));
                            disp(['SIR: ',num2str(sir(s)),'; Lw:',num2str(laser_linewidth(l)),'; Key:',num2str(pn_key(pnk)),'; Vpeak: ',num2str(vp(n)),'; Vbias',num2str(vbias),'; BER: ',formatted_ber,'; run: ',num2str(cnt),' / 12961']);

                            plot_analysis_window;
                            %                          drawnow;

                        end
                    end
                end
                % save('pam4_level_comp');
                disp('saved_run2');
            end
        end
    end


    % BER plot
    figure(340)
    cols = linspecer(7);
    for m = 1:size(BER,1)
        hold on
        plot(rop,BER(m,:),'DisplayName',['Bias: ',num2str(vb(m)), ' V; PAM', num2str(M)],'LineStyle','--','Color',cols(M/2,:),'LineWidth',1,'Marker','square','MarkerEdgeColor',[1 1 1],'MarkerFaceColor',cols(M/2,:));
    end
    set(gca,'YScale','log');
    legend
    xlabel("ROP in dBm")
   yline(3.8e-3,'DisplayName','FEC');

end

figure(2)
LP_awg.showHere;
LP_modulator.showHere;
LP_opt.showHere;
LP_phd.showHere;
LP_scpe.showHere;

% figure('Name','spectrum')
% tiledlayout(4,1)
% nexttile
% spectrum_plot(E.signal,E.fs,'spectrum');
% nexttile
% spectrum_plot(Opt.signal,Opt.fs,'spectrum');
% nexttile;
% spectrum_plot(Rx_sig.signal,Rx_sig.fs,'spectrum');
% nexttile
% spectrum_plot(EQ_sig.signal,EQ_sig.fs,'spectrum');



% save(['C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\MPI\Investigation_April_2024\','PAM_',num2str(M),'_mpi_',num2str(mpi_path),'_lw_',num2str(laser_linewidth)],"BER");

%
%
% figure(21)
% hold on
% plot(rop,mean(BER),'DisplayName',['Modulation: ',num2str(2*vp/extmodlaser.u_pi*100), ' $\%$'],'LineStyle','-','Color',cols(2,:),'LineWidth',1);
% set(gca,'YScale','log');
% legend
% ylabel("ROP in dBm")
% yline(3.8e-3,'DisplayName','FEC');
%
%
%
%
%
%
%
%
% %check Rx and TX symbols
% figure(101)
% scatter(1:100,Rx_symboldecision.signal(1:100),10,'o');
% hold on
% scatter(1:100,digimod_out.signal(1:100),5,'x');
