classdef Fiber
    %FIBER Summary of this class goes here
    %   Detailed explanation goes here

    properties
        fsimu
        fiber_length
        alpha
        D
        Dslope
        lambda0
        gamma
        dphimax

        b2
        b3
        alpha_lin
        linstep
    end

    methods
        function obj = Fiber(options)
            %FIBER Construct an instance of this class
            %   Detailed explanation goes here
            arguments
                options.fsimu
                options.fiber_length = 0
                options.alpha = 0.2
                options.D = 17
                options.Dslope = 0.06
                options.lambda0 = 1550
                options.gamma = 0
                options.dphimax = 5e-3
            end

            obj.fsimu           = options.fsimu;
            obj.fiber_length    = options.fiber_length*1000; %km
            obj.alpha           = options.alpha;
            obj.D               = options.D*1e-6;
            obj.Dslope          = options.Dslope*1e3;
            obj.lambda0         = options.lambda0*1e-9;
            obj.gamma           = options.gamma;
            obj.dphimax         = options.dphimax;



        end

        function signalclass_out = process(obj,signalclass_in)

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

            % append to logbook
            lbdesc = 'Fiber ';
            signalclass_in  = signalclass_in.logbookentry(lbdesc);

            % write to output
            signalclass_out = signalclass_in;

        end

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

            obj.b2 = -obj.D*obj.lambda0^2/(2*pi*Constant.LightSpeed);
            obj.b3 = ((obj.lambda0.^2/(2*pi*Constant.LightSpeed)).^2*obj.Dslope);
            obj.alpha_lin = obj.alpha/10*log(10)/1000;

            N = length(opt_in);
            faxis = linspace(-obj.fsimu/2,obj.fsimu/2,N+1);
            faxis = ifftshift(faxis(:,1:end-1));
            faxis = faxis';

            obj.linstep = -obj.alpha_lin/2 - 2*1j*pi^2*obj.b2*faxis.^2 - 4/3*1j*pi^3*obj.b3*faxis.^3;

            if obj.gamma ~= 0
                opt_out = obj.NLSE(opt_in);
            else
                opt_out = ifft(fft(opt_in).*exp(obj.linstep*obj.fiber_length)); % only one linear step
            end
            %attenuate nase

        end

        function [yout] = NLSE(obj, xin)

            maxPow = obj.gamma.*max(abs(xin).^2);

            Leff = obj.dphimax / maxPow ;

            dz = Leff;

            z_prop = 0;

            yout = fft(xin);

            yout = ((yout).*exp(obj.linstep*dz/2));

            while true

                yout = ifft(yout);

                Leff = dz;

                power = abs(yout).^2;
                Hnl = exp( -1j*obj.gamma*power*Leff);
                yout = yout .* Hnl;

                z_prop = z_prop + dz;

                maxPow = obj.gamma*max(abs(yout).^2);
                Leff = obj.dphimax/maxPow;

                dz_new = Leff;

                if z_prop + dz_new > obj.fiber_length
                    dz_new = obj.fiber_length - z_prop;
                    break
                end

                yout = fft(yout);

                yout = ((yout).*exp(obj.linstep*(dz/2+dz_new/2)));

                dz = dz_new;

            end

            yout = fft(yout);

            yout = ((yout).*exp(obj.linstep*(dz/2+dz_new/2)));

            yout = ifft(yout);

            Leff = dz;

            power = abs(yout).^2;

            Hnl = exp( -1j*obj.gamma*power*Leff);

            yout = yout .* Hnl;

            yout = fft(yout);

            yout = ((yout).*exp(obj.linstep*(dz/2)));

            yout = ifft(yout);


        end


    end
end















