classdef balunCoupledLine < rfpcb.PCBComponent & rfpcb.PrintedLine
%   balunCoupledLine creates a multi-section coupled line balun with unbalanced
%   input and balanced output with 180 degree phase difference.
%
%   h = balunCoupledLine creates a coupled line balun in microstrip form. 
%   The default substrate is FR4 with a thickness of 0.0013 m. The resonant
%   frequency is around 2.96 GHz.
%
%   h = balunCoupledLine ('Property1', value1,'Property2', value2,...)
%   returns a coupled line balun, based on the specified properties.
%   Properties you do not specify retain their default values.
%
%   balunCoupledLine properties:
%
%   NumCoupledLineSection   - Number of coupled line sections. The minimum
%                             number of section is 2 and maximum is 5. The default 
%                             value is 3.
%   CoupledLineLength       - Length of the coupled line in meter. The default
%                             value is 0.01526 m.
%   CoupledLineWidth        - Width of the coupled line in meter. The default
%                             value is 0.0004 m.
%   CoupledLineSpacing      - Spacing between the coupled line in meter. 
%                             The default value is 0.00014 m.
%   UncoupledLineShape      - Shape of the uncoupled line section. This property
%                             accepts ubendMitered. The default dimensions of
%                             ubendMitered have a length of [0.0082 0.00453
%                             0.0082] m, Width of [0.002 0.002 0.002] m and 
%                             MiterDiagonal of 0.002828 m.
%   OutputLineLength        - Length of the quarter wave transformer (bendMitered)
%                             used to extend output ports in meter. The default
%                             value is 0.0124 m. 
%   OutputLineWidth         - Width of the output line in meter. The default
%                             value is 0.000153 m. 
%   OutputLineSpacing       - Spacing between the output ports in meter.
%                             The default value is 0.011 m.
%   Height                  - Height from the coupled line balun to the ground
%                             plane in meter. The default value is 0.0013 m.
%   GroundPlaneWidth        - Width of the ground plane in meter. The default
%                             value is 0.0200 m.
%   Conductor               - Type of metal used for the conducting layers
%                             (accepts an object). The default object is PEC.
%   Substrate               - Type of dielectric material (accepts an object). 
%                             The default dielectric object is FR4.
%
%   balunCoupledLine methods:
%
%   show                    - Visualize the coupled line balun
%   sparameters             - Calculate S-parameter of the coupled line
%                             balun
%   mesh                    - Change or display the mesh properties for the
%                             coupled line balun
%   current                 - Calculate and/or plot the current distribution
%   charge                  - Calculate and/or plot the charge distribution
%   feedCurrent             - Calculate the current at the feed port
%   layout                  - Display all the metal layers along with feed and
%                             vias
%   shapes                  - Return all the shapes of the metal layers in PCB
%   designCoupledLine       - Design the coupled line section of the
%                             balun
%   designUncoupledLine     - Design the uncoupled line section of the
%                             balun
%   designOutputLine        - Design the output line of the balun
%   dgs                     - Create a defected ground structure for
%                             coupled line balun
%
%
%   % Examples
%
%   % Example 1:
%   % Create a coupled line balun with a OutputLineSpacing of 5 mm and visualize it.
%
%   balun = balunCoupledLine('OutputLineSpacing',0.005);
%   show(balun);
%
%   % Example 2:
%   % Create a coupled line balun at the interface of the multi-layer
%   % dielectric and visualize it.
%
%   balun = balunCoupledLine;
%   balun.Substrate = dielectric('Name',{'Teflon','Teflon'},'EpsilonR', ...
%   [2.1 2.1],'LossTangent',[0 0],'Thickness',[0.8e-3 0.8e-3]);
%   balun.Height = 0.8e-3;
%   show(balun);
%
%   See also coupledMicrostripLine, MicrostripLine

%   Copyright 2021-2022 The MathWorks, Inc.

    properties(SetAccess=protected,GetAccess = public)
        % Width of the feed
        FeedDiameter
        % Width of the Via
        ViaDiameter
        % Length of the ground plane
        GroundPlaneLength
    end
    properties (SetObservable)
        % Number of coupled line sections. The minimum number of section is
        % 2 and maximum is 5.
        NumCoupledLineSection
        % Length of the coupled line in meter.
        CoupledLineLength
        % Width of the coupled line in meter.
        CoupledLineWidth
        % Spacing between the coupled line in meter. 
        CoupledLineSpacing
        % Shape of the uncoupled line section. This property accepts 
        % ubendMitered.
        UncoupledLineShape
        % Length of the quarter wave transformer (bendMitered) used to 
        % extend output ports in meter.
        OutputLineLength
        % Width of the output line in meter. 
        OutputLineWidth
        % Spacing between the output ports in meter.
        OutputLineSpacing
        % Width of the ground plane in meter.
        GroundPlaneWidth
    end

    methods
        function obj = balunCoupledLine(varargin)
            parserObj = inputParser;
            UnCoupledLine = ubendMitered;
            UnCoupledLine.Length  = [8.2e-3,4.53e-3,8.2e-3];
            UnCoupledLine.Width   = [2e-3,2e-3,2e-3];
            UnCoupledLine.MiterDiagonal =  sqrt(UnCoupledLine.Width(1)^2+UnCoupledLine.Width(1)^2);
            addParameter(parserObj,'NumCoupledLineSection',3);
            addParameter(parserObj,'CoupledLineLength',15.26e-3);
            addParameter(parserObj,'CoupledLineWidth',0.4e-3);
            addParameter(parserObj,'CoupledLineSpacing',0.14e-3);
            addParameter(parserObj,'UncoupledLineShape',UnCoupledLine);
            addParameter(parserObj,'OutputLineLength',12.4e-3);
            addParameter(parserObj,'OutputLineWidth', 0.153e-3);
            addParameter(parserObj,'OutputLineSpacing',11e-3);
            addParameter(parserObj,'Height',1.3e-3);
            addParameter(parserObj,'GroundPlaneWidth',20e-3); 
            addParameter(parserObj,'GroundPlaneLength',0.069);
            addParameter(parserObj,'Conductor', metal('PEC'));
            addParameter(parserObj,'Substrate',dielectric('Name',{'FR4'},...
                'EpsilonR',4.5,'LossTangent',0.002,'Thickness',1.3e-3));
            addParameter(parserObj,'Tilt',0);
            addParameter(parserObj,'TiltAxis',[0 0 1]);

            parse(parserObj,varargin{:});
            obj = obj@rfpcb.PCBComponent(parserObj.Results.Height,...
                parserObj.Results.Substrate,parserObj.Results.Conductor,...
                parserObj.Results.Tilt,parserObj.Results.TiltAxis);
            obj.NumCoupledLineSection   = parserObj.Results.NumCoupledLineSection;
            obj.CoupledLineLength       = parserObj.Results.CoupledLineLength;
            obj.CoupledLineWidth        = parserObj.Results.CoupledLineWidth;
            obj.CoupledLineSpacing      = parserObj.Results.CoupledLineSpacing;
            obj.UncoupledLineShape      = parserObj.Results.UncoupledLineShape;
            obj.OutputLineLength        = parserObj.Results.OutputLineLength;
            obj.OutputLineWidth         = parserObj.Results.OutputLineWidth;
            obj.OutputLineSpacing       = parserObj.Results.OutputLineSpacing;
            obj.GroundPlaneWidth        = parserObj.Results.GroundPlaneWidth;
            obj.GroundPlaneLength        = parserObj.Results.GroundPlaneLength;
            obj.Tilt                    = parserObj.Results.Tilt;
            obj.TiltAxis                = parserObj.Results.TiltAxis;
            setSubstrateLength(obj,obj.Substrate);
            setSubstrateWidth(obj,obj.Substrate);

            % Assign feed type for model
            setFeedType(obj,'singleedge');
            obj.SolverStruct.Source.type = 'voltage';
            setSourceVoltage(obj, 1, 3);
            setPhaseShift(obj, 0, 3);
        end
        function set.NumCoupledLineSection(obj,propVal)
            validateattributes(propVal,{'double'},...
                {'nonempty','scalar','nonnan','finite','real','integer',...
                '>=', 2, '<=',5},'balunCoupledLine','NumCoupledLineSection');
            if isPropertyChanged(obj,obj.NumCoupledLineSection,propVal)
                obj.NumCoupledLineSection= propVal;
                setSubstrateLength(obj,obj.Substrate);
            end
        end
        function set.CoupledLineLength(obj,propVal)
            validateattributes(propVal,{'numeric'},...
                {'nonempty','scalar','real','nonnan','finite','positive'}...
                ,'balunCoupledLine','CoupledLineLength');
            if isPropertyChanged(obj,obj.CoupledLineLength,propVal)
                obj.CoupledLineLength =  propVal;
                setSubstrateLength(obj,obj.Substrate)
            end
        end
        function set.CoupledLineWidth(obj,propVal)
            validateattributes(propVal,{'numeric'},...
                {'nonempty','scalar','real','nonnan','finite','positive'}...
                ,'balunCoupledLine', 'CoupledLineWidth');
            if isPropertyChanged(obj,obj.CoupledLineWidth,propVal)
                obj.CoupledLineWidth =  propVal;
                setSubstrateWidth(obj,obj.Substrate)
            end
        end
        function set.CoupledLineSpacing(obj,propVal)
            validateattributes(propVal,{'numeric'},...
                {'nonempty','scalar','real','nonnan','finite','positive'}...
                ,'balunCoupledLine','CoupledLineSpacing');
            if isPropertyChanged(obj,obj.CoupledLineSpacing,propVal)
                obj.CoupledLineSpacing =  propVal;
                setSubstrateLength(obj,obj.Substrate);
                setSubstrateWidth(obj,obj.Substrate)
            end
        end
        function set.UncoupledLineShape(obj,propVal)
            if ~(isa(propVal,'antenna.Shape'))
                error(message('rfpcb:rfpcberrors:InvalidShape','UncoupledLineShape'));
            end
            if ~ismember(class(propVal),{'ubendMitered'})
                error(message('rfpcb:rfpcberrors:Unsupported','This Shape',...
                    'balunCoupledLine. Supported Shape is ubendMitered'));
            end
            setHasStructureChanged(obj);
            if isPropertyChanged(obj,obj.UncoupledLineShape,propVal)
                obj.UncoupledLineShape = propVal;
                setSubstrateLength(obj,obj.Substrate);
                setSubstrateWidth(obj,obj.Substrate);
            end
           
        end
        function set.OutputLineLength(obj,propVal)
            validateattributes(propVal,{'numeric'},...
                {'nonempty','scalar','real','nonnan','finite','positive'},...
                'balunCoupledLine','OutputLineLength');
            if isPropertyChanged(obj,obj.OutputLineLength,propVal)
                obj.OutputLineLength= propVal;
                setSubstrateLength(obj,obj.Substrate);
                setSubstrateWidth(obj,obj.Substrate)
            end
        end
        function set.OutputLineWidth(obj,propVal)
            validateattributes(propVal,{'numeric'},...
                {'nonempty','scalar','real','nonnan','finite'},...
                'balunCoupledLine','OutputLineWidth');
            if isPropertyChanged(obj,obj.OutputLineWidth,propVal)
                obj.OutputLineWidth= propVal;
                setSubstrateLength(obj,obj.Substrate)
                setSubstrateWidth(obj,obj.Substrate)
            end
        end
        function set.OutputLineSpacing(obj,propVal)
            validateattributes(propVal,{'numeric'},...
                {'nonempty','scalar','real','nonnan','finite'},...
                'balunCoupledLine','OutputLineSpacing');
            if isPropertyChanged(obj,obj.OutputLineSpacing,propVal)
                obj.OutputLineSpacing= propVal;
                 setSubstrateWidth(obj,obj.Substrate)
                 setSubstrateLength(obj,obj.Substrate)
            end
        end
        function set.GroundPlaneWidth(obj,propVal)
            validateattributes(propVal,{'numeric'},...
                {'nonempty','scalar','real','nonnan','finite','positive'}...
                ,'balunCoupledLine', 'GroundPlaneWidth');
            if isPropertyChanged(obj,obj.GroundPlaneWidth,propVal)
                obj.GroundPlaneWidth =  propVal;
                setSubstrateWidth(obj,obj.Substrate)
            end
        end
        function propVal = get.GroundPlaneLength(obj)
            if ~(isempty(obj.OutputLineLength & obj.OutputLineSpacing & obj.OutputLineWidth))
                
                N   = obj.NumCoupledLineSection;
                L   = obj.CoupledLineLength;
                s   = obj.CoupledLineSpacing;
                ucW = obj.UncoupledLineShape.Width(1);
                ucL = obj.UncoupledLineShape.Length(2);
                PW  = obj.OutputLineWidth;
                
                if obj.OutputLineSpacing == 0
                    propVal = (N*L)+(N-1)*(2*ucW+ucL);
                else
                    PL1 = (obj.OutputLineSpacing-s)/2;
                    PL2 = (obj.OutputLineLength-PL1);
                    % Error condition to check if output Line Length is lesser than
                    % the vertical length of the bend line at the output
                    if  obj.OutputLineLength<PL1
                        error(message('rfpcb:rfpcberrors:InvalidValueGreater',...
                            'OutputLineLength',num2str(PL1)));
                    end
                    propVal = (N*L)+(N-1)*(2*ucW+ucL)+PW/2+PL2;
                end
            else
                propVal=[];
            end
        end
    end

    methods (Hidden)

        function [edgeLength,growthRate] = calculateMeshParams(obj,lambda)
            createGeometry(obj);
            Aboard = area(obj.privateStack.BoardShape);
            metalLayers = find(cellfun(@(x) isa(x,'antenna.Shape'),obj.privateStack.Layers));
            A_phys  = area(obj.privateStack.Layers{metalLayers(1)});
            epsr    = obj.Substrate.EpsilonR;
            lambda  = lambda/sqrt(mean(epsr));
            [edgeLength,minel,~] = calcMeshParamsCore(obj,lambda, Aboard, A_phys, 0.3);
            growthRate = 1.5;
            setMeshMinContourEdgeLength(obj,minel);
        end

        function createGeometry(obj,varargin)
            setSubstrateLength(obj,obj.Substrate);
            setSubstrateWidth(obj,obj.Substrate);
            % Get the tolerance value based on the dimensions of the shape
            tol = getTolerance(obj);
            % Check uncoupledline dimensions
            ucw = round(obj.UncoupledLineShape.Width,tol);
            ucl = round(obj.UncoupledLineShape.Length,tol);
            UW = ucw(1,:)==ucw(1);
            UL = ucl(1,1)==ucl(1,3);
            if ~all(UW)
                error(message('rfpcb:rfpcberrors:InvalidUncoupledLineWidth'));
            end
            if ~all(UL)
                error(message('rfpcb:rfpcberrors:InvalidUncoupledLineLength'));
            end
            
            % Check if user defined height is one among the layers
            layerHeights = round(cumsum(obj.Substrate.Thickness),12);
            NumSubLayer =  numel(layerHeights);
            A = find(round(abs(layerHeights-obj.Height),15) <= eps(max(abs(layerHeights),abs(obj.Height))), 1);
            if isempty(A)
                 Height = layerHeights(1,1:NumSubLayer);
                 error(message('rfpcb:rfpcberrors:InvalidHeight',num2str(Height)));
            end

            gndL = obj.GroundPlaneLength;
            gndW = obj.GroundPlaneWidth;

            % Create coupled line balun
            Cbalun = CreateBalun(obj);

            % Create ground plane
            gndP = traceRectangular('Length',gndL,'Width',gndW,'Center',[0 0]);
            GVer = round(gndP.getShapeVertices,tol);
            gndPlane =  traceRectangular('Length',2*abs(GVer(1,1)),'Width',2*abs(GVer(1,2)));

            setFeedType(obj,'singleedge');

            % Setting feed diameter to half of the input and output line
            % width
            if obj.OutputLineSpacing == 0
                obj.FeedDiameter = round(obj.CoupledLineWidth/2,tol);
            else
                obj.FeedDiameter = round([obj.CoupledLineWidth/2,obj.OutputLineWidth/2,obj.OutputLineWidth/2],tol);
            end

            obj.ViaDiameter  = round(obj.CoupledLineWidth/2,tol);

            diel = buildSubstrate(obj);

            % Create the pcb stack
            MetalLayers = GetMetalLayer(obj,Cbalun,gndPlane);

            subL = obj.Substrate.Length;
            subW = obj.Substrate.Width;
            bshape = traceRectangular('Length',subL,'Width',subW);
            BsVer = round(bshape.getShapeVertices,tol);
            boardshape =  traceRectangular('Length',2*abs(BsVer(1,1)),'Width',2*abs(BsVer(1,2)));

            buildStack(obj,'Coupled Line Balun', MetalLayers, diel, boardshape, 1, 1);
            
            % Save the geometry
            geom        = saveStackGeom(obj);

            % Set feed width
            feed_width      = obj.privateStack.FeedWidth;
            obj.FeedWidth   = feed_width;
            setFeedWidth(obj, feed_width);
            savePrintedAntennaGeometry(obj,geom);

        end%of createGeometry
        function meshGenerator(obj,varargin)

            createGeometry(obj);

            % Check if length of the coupled line balun along width is
            % within the ground plane width
            Len = max([2*(obj.UncoupledLineShape.Length(1)),...
                (obj.OutputLineSpacing+obj.OutputLineWidth),2*obj.CoupledLineWidth]);
            gndW = obj.CoupledLineSpacing+Len;

            if  obj.GroundPlaneWidth <= gndW
                error(message('rfpcb:rfpcberrors:InvalidValueGreater',...
                    'GroundPlaneWidth',num2str(gndW)));
            end

            % Check if width of the coupled line balun is less than
            % width of the uncoupled line shape
            if  obj.CoupledLineWidth > obj.UncoupledLineShape.Width(1)
                error(message('rfpcb:rfpcberrors:InvalidValueLess',...
                    'obj.CoupledLineWidth',num2str(obj.UncoupledLineShape.Width(1))));
            end
            if obj.OutputLineSpacing ~= 0
                md = sqrt(obj.OutputLineWidth^2+obj.OutputLineWidth^2);
                MaxSpacing  = obj.CoupledLineSpacing+2*(obj.CoupledLineWidth+md);
                if  obj.OutputLineSpacing < MaxSpacing
                    error(message('rfpcb:rfpcberrors:InvalidValueGreater',...
                        'OutputLineSpacing',num2str(MaxSpacing)));
                end
            end
            s = getMeshEdgeLength(obj);
            gr= getMeshGrowthRate(obj);
            minel = getMinContourEdgeLength(obj);
            minel = fixMinEdgeLength(obj,s,minel);
            printedmesh = meshStack(obj, s, gr,minel);
            setFeedType(obj,getFeedType(obj.privateStack));
            saveMesh(obj, printedmesh.Mesh);
        end

        function rObj = superLoad(obj,s)
            if nargin>1
                obj.NumCoupledLineSection    = s.NumCoupledLineSection;
                obj.CoupledLineLength        = s.CoupledLineLength;
                obj.CoupledLineWidth         = s.CoupledLineWidth;
                obj.CoupledLineSpacing       = s.CoupledLineSpacing;
                obj.UncoupledLineShape       = s.UncoupledLineShape;
                obj.OutputLineLength         = s.OutputLineLength;
                obj.OutputLineWidth          = s.OutputLineWidth;
                obj.OutputLineSpacing        = s.OutputLineSpacing;
                obj.GroundPlaneWidth         = s.GroundPlaneWidth;
                obj.privateStack = s.privateStack;
                rObj = superLoadPCBComponent(obj,s,{'Gnd','Feed','Rad'});
            end
        end
    end

    methods(Hidden)
        % Override superclass method(from em.Antenna specific to this
        % element since there is control on feed location
        function feedpoint = GetFeedPoint(obj)
            layerHeights = round(cumsum(obj.Substrate.Thickness),12);
            if obj.Height < max(layerHeights)
                % Embedded Microstrip Configuration - The metal can be between any
                % dielectric layer
                % Calculates the metal layer position for the GroundPlane
                % and the top metal layer for passing to the feed point.
                substrateIndex  = find(round(abs(layerHeights-obj.Height),15) <= eps(max(abs(layerHeights),abs(obj.Height))));
                tempLayers                     	= numel(cell(1,numel(layerHeights) + 2));
                metalLayer1                   	= tempLayers;
                metalLayer2                    	= tempLayers-1-substrateIndex;

            else
                % Microstrip Configuration
                tempLayers                     	= numel(cell(1,numel(layerHeights) + 2));%{topLayer}
                metalLayer1                    	= tempLayers;
                substrateIndex                 	= find(layerHeights<=obj.Height);
                metalLayer2                    	= tempLayers-1-numel(substrateIndex);
            end
            tol = getTolerance(obj);
            if obj.OutputLineSpacing == 0
                feedy1 = round(obj.CoupledLineWidth/2 + obj.CoupledLineSpacing/2,tol);
                feedy2 = round(feedy1,tol);
            else
                PL1    = round((obj.OutputLineSpacing-obj.CoupledLineSpacing)/2,tol);
                feedy1 = round(obj.CoupledLineWidth/2 + obj.CoupledLineSpacing/2,tol);
                feedy2 = round(PL1+obj.CoupledLineSpacing/2,tol);
            end
            feedpoint  =  round([-obj.GroundPlaneLength/2 feedy1 obj.Height metalLayer2 metalLayer1;...
                obj.GroundPlaneLength/2 feedy2 obj.Height metalLayer2 metalLayer1;...
                obj.GroundPlaneLength/2 -feedy2 obj.Height metalLayer2 metalLayer1],tol);
        end

        function MetalLayers = GetMetalLayer(obj,topLayer,gndPlane)

            layerHeights = round(cumsum(obj.Substrate.Thickness),12);
            if obj.Height < max(layerHeights)
                % StripLine Configuration - The metal can be between any
                % dielectric layer
                substrateIndex  = find(round(abs(layerHeights-obj.Height),15) <= eps(max(abs(layerHeights),abs(obj.Height))));
                tempLayers                       = cell(1,numel(layerHeights) + 2);
                tempLayers{end}                  = gndPlane;
                tempLayers{end-1-substrateIndex} = topLayer;
                MetalLayers = tempLayers;
            else
                % Microstrip Configuration
                tempLayers                               = cell(1,numel(layerHeights) + 2);%topLayer
                tempLayers{end}                          = gndPlane;
                substrateIndex                           = find(layerHeights<=obj.Height);
                tempLayers{end-1-numel(substrateIndex)}  = topLayer;
                MetalLayers                              = tempLayers;
            end
        end
        function Viapoint = GetViaPoint(obj)
            tol = getTolerance(obj);
            toplayer    = CreateBalun(obj);
            gnd = traceRectangular('Length',obj.GroundPlaneLength,'Width',obj.GroundPlaneWidth);
            Mlayers     = GetMetalLayer(obj,toplayer,gnd);
            metalLayers = find(cellfun(@(x) isa(x,'antenna.Shape'),Mlayers));
            y = obj.CoupledLineSpacing/2+obj.CoupledLineWidth/2;
            Viapoint = round([-obj.GroundPlaneLength/2  -y  metalLayers(1) metalLayers(2)],tol);
        end
    end
    methods(Hidden)
        function CLineBalun = CreateBalun(obj)

            N    = obj.NumCoupledLineSection;
            L    = obj.CoupledLineLength;
            W    = obj.CoupledLineWidth;
            s    = obj.CoupledLineSpacing;
            ucL  = obj.UncoupledLineShape.Length;
            ucW  = obj.UncoupledLineShape.Width;
            outLineLength = obj.OutputLineLength;
            outLineSpacing = obj.OutputLineSpacing;
            PW  = obj.OutputLineWidth; 
            PL1 = (outLineSpacing-s)/2;
            PL2 = (outLineLength-PL1);
            gndL = obj.GroundPlaneLength;
            tol = getTolerance(obj);
            % Error conditions
            % Error condition to check if coupled line length is greater
            % than coupled line width
            if  L <=  W
                error(message('rfpcb:rfpcberrors:InvalidValueGreater',...
                    'CoupledLineLength',num2str(obj.CoupledLineWidth)));
            end
            if outLineSpacing ~= 0
                % Error condition to check if coupled line spacing is greater
                % than output line spacing
                if  s >=  outLineSpacing
                    error(message('rfpcb:rfpcberrors:InvalidValueLess',...
                        'CoupledLineSpacing',num2str(outLineSpacing)));
                end
                
                % Error condition to check if outLineWidth is greater than
                % horizontal length of the bend line at the output
                if   PW >= PL2
                    error(message('rfpcb:rfpcberrors:InvalidValueLess',...
                        'OutputLineWidth',num2str(PL2)));
                end
                % Error condition to check if outLineWidth is greater than
                % vertical length of the bend line at the output
                if   PW >= PL1
                    error(message('rfpcb:rfpcberrors:InvalidValueLess',...
                        'OutputLineWidth',num2str(PL1)));
                end
            end

            % Create coupled line section
            trace1 = {};
            trace2 = {};
            Cline = {};
            for i = 1:N
                xcen = -gndL/2+(i-1)*L+L/2+(i-1)*2*ucW(1)+(i-1)*ucL(2);
                ycen = s/2+W/2;
                trace1{i} = traceRectangular('Length',L,'Width',W,'Center',[xcen,ycen]);
                trace2{i} = traceRectangular('Length',L,'Width',W,'Center',[xcen,-ycen]);
                Cline{i} = trace1{i}+trace2{i};
            end

            % Create uncoupled line section
            ubend1 = {};
            ubend2 = {};
            UnCline = {};
            for i = 1:N-1
                md = sqrt(ucW(1)^2+ucW(1)^2);
                ubend = ubendMitered('Width',ucW,'Length',ucL,'MiterDiagonal', md);
                xt = -gndL/2+(i*L)+((i-1)*(2*ucW(1)+ucL(2)))+(ucW(1)+ucL(2)/2);
                yt = s/2+ucL(1);
                zt = 0;
                ubend1{i} = copy(ubend);
                ubend1{i} = rotateZ(ubend1{i},180);
                ubend1{i} = translate(ubend1{i},[xt,yt,zt]);

                ubend2{i} = copy(ubend);
                ubend2{i} = translate(ubend2{i},[xt,-yt,zt]);
                UnCline{i} = ubend1{i} + ubend2{i};
            end

            % Sum of coupled and uncoupled line sections
            SumTraces = Cline{1}+UnCline{1};
            if N ==2
                SumTraces = SumTraces+Cline{N};
            else
                for i = 2:1:N-1
                    SumTraces = SumTraces+Cline{i}+UnCline{i}+Cline{N};
                end
            end
            ClineBalun = SumTraces;

            % Create output ports
            if obj.OutputLineSpacing ==0
                CLineBalun = ClineBalun;
            else
                md = sqrt(PW^2+PW^2);
                b = bendMitered('Length',[PL1 PL2],'MiterDiagonal',md,'Width',[PW,PW]);

                % Create port 2
                Zt50 = copy(b);
                Zt50 = rotateZ(Zt50,90);
                xpt = -gndL/2+(N*L)+PW/2+(N-1)*(2*(ucW(1))+ucL(2));
                ypt = PL1+s/2;
                Zt50 = translate(Zt50,[xpt,ypt,0]);

                % Create port 3
                b1 = bendMitered('Length',[PL2 PL1],'MiterDiagonal',md,'Width',[PW,PW]);

                Zd50 = copy(b1);
                Zd50 = rotateZ(Zd50,180);
                xpt = -gndL/2+(N*L)+PW/2+(N-1)*(2*(ucW(1))+ucL(2));
                ypt = PL1+s/2;
                Zd50 = translate(Zd50,[xpt,-ypt,0]);   

                % Coupled line balun structure along with extented output 
                % line for ports
                Balun = ClineBalun+Zd50+Zt50;
                CVer = round(Balun.Vertices,tol);
                CLineBalun = antenna.Polygon('Vertices',CVer);
            end
        end
    end
    methods
        function [ClineLen,ClineWid,ClineSpac] = designCoupledLine(dobj,freq,options) 
            %designCoupledLine method calculates the dimensions of the 
            %coupled line section. This is the first step in designing the balun.
            %
            %   [ClineLen,ClineWidth,ClineSpac] = designCoupledLine(dobj,1e9,'Z0e',159,'Z0o',51) 
            %   returns a length, width and spacing of the coupled line
            %   section.
            %
            %   Input Arguments:
            %       dobj            : coupled line balun object.
            %       Frequency       : Design frequency. 
            %       Z0e             : Even mode impedance in ohm. The default
            %                         value is 159.
            %       Z0o             : Odd mode impedance in ohm. The default
            %                         value is 51.
            %
            %   Output Arguments:
            %       ClineLen  : Length of the coupled line section (CoupledLineLength).
            %       ClineWid  : Width of the coupled line section (CoupledLineWidth).
            %       ClineSpac : Spacing between the coupled line section (CoupledLineSpacing).
            %
            %   % Examples:
            %
            %   % Example 1:
            %   % Design the coupled line section at 4 GHz with an even
            %   % mode impedance of 159 ohm and odd mode impedance of 51 ohm.
            %
            %   [L,W,S]= designCoupledLine(balunCoupledLine,4e9,'Z0e',159,'Z0o',51);
            %
            %   See also designUncoupledLine, designOutputLine
            %
            arguments
                dobj
                freq (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty}
                options.Z0e (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty} = 159;
                options.Z0o (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty} = 51;
            end
            
            obj = copy(dobj);
            if numel(obj.Substrate.Thickness)>1
                error(message('rfpcb:rfpcberrors:Unsupported',...
                    'Design of Coupled line section','multiple substrates'));
            end

            Zoe = options.Z0e;
            Zoo = options.Z0o;          
            m = copy(coupledMicrostripLine);
            m.Height = obj.Height;
            m.Substrate = obj.Substrate;
            d1 = design(m,freq,'Z0e',Zoe,'Z0o',Zoo);
            ClineLen   = d1.Length;
            ClineWid   = d1.Width;
            ClineSpac  = d1.Spacing;
            if nargout~=3
                error(message('rfpcb:rfpcberrors:IncorrectNumArguments','output','output','3'));
            end
        end
        function [UClineLen,UClineWid] = designUncoupledLine(dobj,freq,options)
            %designUncoupledLine method calculates the dimensions of the 
            %uncoupled line section. This is the second step in designing the balun.
            %
            %   [UClineLen,UClineWidth] = designUncoupledLine(dobj,1e9,'Z0',50,'LineLength',0.25) 
            %   returns a length and width of the uncoupled line section.
            %
            %   Input Arguments:
            %       dobj            : coupled line balun object.
            %       Frequency       : Design frequency. 
            %       Z0              : Impedance of the uncoupled line in ohms. 
            %                         The default value is 50.
            %       LineLength      : Length of the uncoupled line in terms of
            %                         multiples of lambda. The default
            %                         value is 0.25.
            %
            %   Output Arguments: 
            %       UClineLen   : Length of the uncoupled line section (uncoupledLineShape.Length).
            %       UClineWid   : Width of the uncoupled line section (uncoupledLineShape.Width).
            %       
            %   % Examples:
            %
            %   % Example 1:
            %   % Design for the uncoupled line section at 4 GHz with an even
            %   % mode impedance and odd mode impedance of 59 ohm.
            %
            %   [UClineLen,UClineWid]= designUncoupledLine(balunCoupledLine,4e9,'Z0',59,'LineLength',0.25);
            %
            %   See also designCoupledLine, designOutputLine
            %
            arguments
                dobj
                freq (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty}
                options.Z0 (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty,mustBeInRange(options.Z0,10,300)} = 50;
                options.LineLength (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty} = 0.25
            end
            obj      = copy(dobj);
             if numel(obj.Substrate.Thickness)>1
                error(message('rfpcb:rfpcberrors:Unsupported',...
                    'Design of Uncoupled line section','multiple substrates'));   
            end
            m        = copy(microstripLine);
            m.Height = obj.Height;
            Z0       = options.Z0;
            LineLength  = options.LineLength;
            m.Substrate = obj.Substrate;
            d1 = design(m,freq,'Z0',Z0,'LineLength',LineLength);
            UClineLen  = d1.Length;
            UClineWid  = d1.Width;   
            if nargout~=2
                error(message('rfpcb:rfpcberrors:IncorrectNumArguments','output','output','2'));
            end
        end
        function [OutLength,OutWidth] = designOutputLine(dobj,freq,options) 
            %designOutputLine method calculates the dimensions of the 
            %output line of the balun. This is the third step in designing the balun.
            %
            %   [OutLength,OutWidth] = designOutputLine(dobj,1e9,'Z0e',159,'Z0o',51,'Z0',59,'Zref',50) 
            %   returns a length and width of the output line.
            %
            %   Input Arguments:
            %       dobj           : coupled line balun object.
            %       Frequency      : Design frequency.
            %       Z0e            : Even mode Impedance of the coupled line
            %                        section in ohms. The default value is
            %                        159.
            %       Z0o            : Odd mode Impedance of the coupled line
            %                        section in ohms. The default value is
            %                        51.
            %       Z0             : Impedance of the uncoupled line in ohms.
            %                        The default value is 59.
            %       Zref           : Reference impedance to match the output. 
            %                        The default value is 50.
            %
            %   Output Arguments: 
            %       OutLength   : Length of the output line section (OutputLineLength).
            %       OutWidth    : Width of the output line section (OutputLineWidth).
            %
            %   Note: The even mode impedance (Z0e), odd mode impedance
            %         (Z0o) and impedance of the uncoupled line section (Z0)
            %         should be the one that is used to design coupled line and
            %         uncoupled line section respectively.
            %
            %   % Examples:
            %
            %   % Example 1:
            %   % Design output line of the coupled line balun at 4 GHz with even mode and 
            %   % odd mode impedance of the coupled line section with 159 and 51 respectively 
            %   % and impedance of uncoupled line section with 59 ohm.   
            %
            %   [OutLength,OutWidth]= designOutputLine(balunCoupledLine,4e9,'Z0e',159,'Z0o',51,'Z0',59,'Zref',50);
            %
            %   See also designCoupledLine, designUncoupledLine
            %
            arguments
                dobj
                freq (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty}
                options.Z0e (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty} = 159;
                options.Z0o (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty} = 51;
                options.Z0 (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty} = 59;
                options.Zref (1,1) {mustBePositive,mustBeFinite,mustBeNonNan,mustBeNonempty} = 50;
            end
            obj = copy(dobj);
             if numel(obj.Substrate.Thickness)>1
                error(message('rfpcb:rfpcberrors:Unsupported',...
                    'Design of Output Line section','multiple substrates'));   
            end
            Z0e = options.Z0e;
            Z0o = options.Z0o;
            Z0  = options.Z0;
            Zref = options.Zref;
            n = obj.NumCoupledLineSection;
            % Total even mode impedance of the balun
            Z0e = (Z0e.^(n))/(Z0.^(n-1));
            % Total odd mode impedance of the balun
            Z0o = (Z0o.^(n))/(Z0.^(n-1));
            Z0 = sqrt(Z0e*Z0o);
            Z = sqrt(2*Z0*Zref);
            m = copy(microstripLine);
            m.Height = obj.Height;
            m.Substrate = obj.Substrate;
            d1 = design(m,freq,'Z0',Z,'LineLength',0.25);
            OutLength = d1.Length;
            OutWidth  = d1.Width; 
            if nargout~=2
                error(message('rfpcb:rfpcberrors:IncorrectNumArguments','output','output','2'));
            end
        end
    end
    methods(Hidden)
        function tol = getTolerance(obj)
             DimMin = min([obj.NumCoupledLineSection,obj.CoupledLineLength,obj.CoupledLineWidth,...
                obj.CoupledLineSpacing,obj.UncoupledLineShape.Length,...
                obj.UncoupledLineShape.Width,obj.OutputLineLength,...
                obj.OutputLineSpacing,obj.OutputLineWidth]);
            
            if DimMin<0.1e-3
                tol = 12;
            else
                tol = 8;
            end
        end
    end

    methods(Access=protected)
        function group = getPropertyGroups(obj)
            title = '';
            p = {'NumCoupledLineSection','CoupledLineLength','CoupledLineWidth',...
                'CoupledLineSpacing','UncoupledLineShape','OutputLineLength','OutputLineWidth','OutputLineSpacing','Height',...
                'GroundPlaneWidth','Substrate','Conductor'};
            group = objectProps(obj,p,title);

        end
    end
   
    methods (Static = true,Hidden) % ----LOADOBJ only IMPLEMENTATION ---%
        function r = loadobj(obj)
            if isobject(obj)&& isObjectFromCurrentVersion(obj)
                r = obj;
            else
                % Create default microstrip Line
                newbalunCoupledLine = balunCoupledLine;
                % Populate the properties
                r = superLoad(newbalunCoupledLine,obj);
            end
            r = loadobj@em.EmStructures(r);
        end
    end

    methods(Hidden)
        function feededge = getFeedingEdges(obj, edges, FeedPoint,P)
            feededge = getFeedingEdges(obj.privateStack, edges, FeedPoint,P);
        end
    end
    
    
end % of Coupled Line balun
