%makeAntennaArrayPhAST make antenna array for CDL channel from PhAST array object
%
%   Note: This is an internal undocumented function and its API and/or
%   functionality may change in subsequent releases.

%   Copyright 2020 The MathWorks, Inc.

%#codegen
function array = makeAntennaArrayPhAST(antennaArray,Orientation,CarrierFrequency)

    persistent c0;
    if isempty(c0)
        c0 = physconst('LightSpeed');
    end
    
    lambda = c0/CarrierFrequency;
    phasedArray = antennaArray;
    array.Position = [0;0;0];

    % In PhaST, the initial array orientation is relative to the LCS.
    % For planar arrays, this is determined by the ArrayNormal so that
    % the rotations specified by Orientation are relative to broadside.
    % For other arrays, the first element normal direction is used as
    % reference for rotation.
    initialArrayOrient = getPhaSTArrayOrientation(phasedArray);
    array.InitialArrayOrientation = initialArrayOrient;

    % Orientation is relative to InitialArrayOrientation
    array.Orientation = Orientation(:);

    % Calculate the LCS->GCS rotation matrix R (TR 38.901 Sect 7.3.1).
    % As the intrinsic rotation is defined in the order Z(bearing),
    % Y(downtilt), X(slant), the matrix R0 rotates the element to align
    % its normal with the X-axis before the intrinsic rotation and the
    % matrix R0' undoes the effects of R0.
    R0 = wireless.internal.channelmodels.rotationMatrix(initialArrayOrient)';
    R = R0'*wireless.internal.channelmodels.rotationMatrix(Orientation)*R0;

    % Transform the element positions from LCS to GCS. PhAST element
    % positions in meters are scaled by the carrier wavelength.
    ep = R*phasedArray.getElementPosition/lambda;

    % For PhAST phased.NRRectangularPanelArray antenna arrays, the element
    % positions and orientations are stored in (3 x M x N x P x M_g x N_g)
    % arrays as in the case of structure antenna arrays
    if isa(phasedArray,'phased.NRRectangularPanelArray')
        MN = phasedArray.Size(1:2);
        P = length(phasedArray.ElementSet);
        M_gN_g = phasedArray.Size(3:4);
        
        array.ElementPositions = reshape(ep, [3 MN P M_gN_g]);
        array.SubarrayPositions = [];

        % For PhAST elements other than phased.NRAntennaElement, set the
        % polarization slant to 0.
        eo = zeros([3 MN P M_gN_g]);
        eo(3,:,:,:,:,:) = wireless.internal.channelmodels.getCDLElementSlant(phasedArray);
        array.ElementOrientations = eo;
    else
        % For other PhAST antenna arrays, element positions, subarray
        % positions, and element orientations are stored in a (3 x
        % NumberOfElements) array
        array.ElementPositions = ep;

        % Determine subarray positions and slant angles
        if isa(phasedArray,'phased.internal.AbstractSubarray')
            array.SubarrayPositions = R*phasedArray.getSubarrayPosition/lambda;
            elemSlant = wireless.internal.channelmodels.getCDLElementSlant(phasedArray);
        else % subclasses of phased.internal.AbstractArray (ULA,URA,UCA,Het*...)
            array.SubarrayPositions = [];
            elemSlant = wireless.internal.channelmodels.getCDLElementSlant(phasedArray);
        end

        array.ElementOrientations = zeros(3,phasedArray.getNumElements);
        array.ElementOrientations(3,:) = elemSlant;
    end
    
end

function arrayOrient = getPhaSTArrayOrientation(array)

    if isa(array, 'phased.internal.AbstractSubarray')
        sub = getSubArray(array);
        arrayOrient = getPhaSTArrayOrientation(sub);
    else
        % phased.internal.AbstractArray
        if isa(array,'phased.URA') || isa(array,'phased.HeterogeneousURA') || isa(array,'phased.UCA')
            arrayNorm = double(array.ArrayNormal=='xyz')';
            [~,elemNorm] = rangeangle(arrayNorm);
        elseif isa(array,'phased.HeterogeneousConformalArray') || isa(array,'phased.ConformalArray')
            elemNorm = array.ElementNormal;
        else
            % Other arrays use the normal to the first element
            elemNorm = array.getElementNormal;
        end
        arrayOrient = [elemNorm(1); -elemNorm(2); 0]; % Bearing, Downtilt, Slant
    end
    
end

function sub = getSubArray(array)
    if isa(array, 'phased.PartitionedArray')
        sub = array.Array;
    else % Replicated
        sub = array.Subarray;
    end
end
