classdef EML
    %EML External Modulated Laser
    %   Detailed explanation goes here
    
    properties
        %class input objms
        mode
        fsimu %in Hz
        lambda %in nm
        power %in dBm
        linewidth %in Hz
        ampl_imbal 
        pha_imbal
        bias 
        u_pi
        randomkey
        randomstream

        %on instance creation
        field
        noisefactor
        phase %eigentlich nur wichtig wenn man mehrere blöcke durchsimuliert...
        real_factor
        imag_factor

        %during simulation
        signal_len

    end
    
    methods
        function obj = EML(options)
            %EML Construct an instance of this class
            %   Detailed explanation goes here
            arguments
                options.mode eml_mode = eml_mode.im_cosinus ;
                options.fsimu;
                options.lambda;
                options.power;
                options.linewidth = 0;
                options.ampl_imbal = 0;
                options.pha_imbal = 0;
                options.bias;
                options.u_pi;
                options.randomkey = 2023;
            end
            
            fn = fieldnames(options);
            for l = 1:numel(fn)
                obj.(fn{l}) = options.(fn{l});
            end

            obj.randomstream = RandStream('mlfg6331_64','Seed',obj.randomkey);

            obj.field=sqrt(10^(obj.power/10-3)); %dbm to sqrt(mw)
            
            obj.noisefactor=sqrt(2*pi*obj.linewidth/obj.fsimu);
            obj.phase=0;

            if obj.mode==4 || obj.mode==5
                obj.real_factor=(1+obj.ampl_imbal/2)*exp(1i*obj.pha_imbal/2);
                obj.imag_factor=(1-obj.ampl_imbal/2)*exp(1i*(pi/2-obj.pha_imbal/2));
            end

        end
        
         function [signalclass_out,obj] = process(obj,signalclass_in)

             % actual processing of the signal (steps 1. - 3.)
            [signalclass_in.signal,obj] = obj.process_(signalclass_in.signal); 

             % cast the inform. signal to electrical signal 
            signalclass_in = Opticalsignal(signalclass_in,"fs",obj.fsimu,"logbook",signalclass_in.logbook,"lambda",obj.lambda*1e-9,"nase",0);

             % append to logbook
             lbdesc = [num2str(obj.lambda),' nm Laser with ',num2str(obj.power),' dBm P_out. Linew.=',num2str(obj.linewidth*1e-6),' MHz. Modulation mode: ',char(obj.mode) ];
            signalclass_in  = signalclass_in.logbookentry(lbdesc);

             % write to output
            signalclass_out = signalclass_in;

        end


        function [opt_out,obj] = process_(obj,elec_in)
            %METHOD1 Summary of this method goes here
            %   Detailed explanation goes here

            %needed later
            obj.signal_len = length(elec_in);

            % generate phase noise of laser
            % scale with signal energy in sqrt(mw)
            if obj.linewidth ~= 0
                ph_noi = obj.createPhaseNoise;
                laserfield = obj.field.*exp(1i*ph_noi);
                %remember phase (! you need to receive the altered eml object in you sim program !)
                obj.phase = ph_noi; 
            else
                laserfield = obj.field;
            end
            
            %modulate the laserfield with the electrical signal
            opt_out = obj.externalmodulation(laserfield,elec_in);



        end

        function noi = createPhaseNoise(obj)

            %create random vector
            noi = randn(obj.randomstream,obj.signal_len,1);

            %scale with noisefactor
            noi = noi * obj.noisefactor;

            %cumsum to accumulate noise over time vector
            noi = cumsum(noi);

        end

        function modulated_laserfield = externalmodulation(obj,laserfield,electrical_in)

            switch obj.mode

                case   1 % linear IM
                    modulated_laserfield = laserfield.*sqrt(real(electrical_in + obj.bias)/obj.u_pi);

                case 2   % IM with hMZM function
                    modulated_laserfield = laserfield.*cos(pi/2*(real(electrical_in)+obj.bias)/obj.u_pi);

                case 3 % PMsi
                    modulated_laserfield = laserfield.*exp(1i*pi*real(electrical_in)/obj.u_pi);

                case 4 % linear IQ-Modulator
                    if obj.ampl_imbal ==0 && obj.pha_imbal  == 0
                        % No IQ Imbalance
                        modulated_laserfield=laserfield.*electrical_in/obj.u_pi;
                    else
                        % with IQ Imbalance
                        modulated_laserfield = laserfield.*(real(electrical_in)*obj.real_factor/obj.u_pi...
                            +imag(electrical_in)*obj.imag_factor/obj.u_pi);
                    end

                case 5 % IQ-Modulator with cosine function
                    modulated_laserfield = laserfield/sqrt(2).*(cos(pi/2*(real(electrical_in)+obj.bias)/obj.u_pi)*obj.real_factor...
                        +cos(pi/2*(imag(electrical_in)+obj.bias)/obj.u_pi)*obj.imag_factor);

            end

        end

    end
end

