%CDLChannelDelayProfileInfo TR 38.901 Delay profile information of CDL channel model

%   Copyright 2020 The MathWorks, Inc.

%#codegen

function [info,pdp] = CDLChannelDelayProfileInfo(model)

    validateParameters(model);
    
    % named column subscripts
    [delay,power,AoD,AoA,ZoD,ZoA] = wireless.internal.channelmodels.namedDelayProfileColumns();
    
    %----------------------------------------------------------------------
    % delay profile setup
    
    % 'pdp' stands for "power delay profile", but also includes columns for
    % AoD, AoA, ZoD and ZoA. 'pdp' is size Lin-by-6 where 'Lin' is the
    % number of clusters and the 6 columns are:
    % [delay,power,AoD,AoA,ZoD,ZoA].
    % The final number of clusters used in the model is L = Lin + (2*NSC)
    % where NSC is the number of strongest clusters to split into
    % sub-clusters (model.NumStrongestClusters), with each split cluster 
    % consisting of three sub-clusters.
    
    pdp = zeros(size(model.PathDelays,2),6);
    pdp(:,delay) = model.PathDelays;
    pdp(:,power) = model.AveragePathGains;
    pdp(:,AoD) = model.AnglesAoD;
    pdp(:,AoA) = model.AnglesAoA;
    pdp(:,ZoD) = model.AnglesZoD;
    pdp(:,ZoA) = model.AnglesZoA;
    
    %----------------------------------------------------------------------
    % delay profile creation for LOS clusters, sub-clustered (split) NLOS
    % clusters and other NLOS clusters
    
    [pdpLOS,info.KFactorFirstCluster] = getLOSClusters(model,pdp);    % size LOS-by-6 where LOS = 0 or 1
    pdpSplitNLOS = getSplitNLOSClusters(model,pdp);                   % size (NSC*3)-by-C where NSC = model.NumStrongestClusters
    pdpNLOS = getNLOSClusters(model,pdp);                             % size (Lin-LOS-NSC)-by-6
    info.ClusterTypes = getClusterTypes(pdpLOS,pdpSplitNLOS,pdpNLOS); % size 1-by-L
    
    %----------------------------------------------------------------------
    % assembly of overall delay profile
    
    pdp = [pdpLOS; pdpSplitNLOS; pdpNLOS];   % size L-by-6
    info.PathDelays = pdp(:,delay).';        % size 1-by-L
    info.AveragePathGains = pdp(:,power).';  % size 1-by-L
    info.AnglesAoD = pdp(:,AoD).';           % size 1-by-L
    info.AnglesAoA = pdp(:,AoA).';           % size 1-by-L
    info.AnglesZoD = pdp(:,ZoD).';           % size 1-by-L
    info.AnglesZoA = pdp(:,ZoA).';           % size 1-by-L
    
end

function [pdp,K_1dB] = getLOSClusters(model,pdp)
    
    % named column subscripts
    [~,power] = wireless.internal.channelmodels.namedDelayProfileColumns();
    
    % extract the LOS cluster (if present)
    LOS = model.HasLOSCluster; % LOS = 0 or 1
    if (LOS && size(pdp,1)>1)
        K_1dB = pdp(1,power) - pdp(2,power);
    else
        K_1dB = -Inf;
    end
    pdp = pdp((2-LOS):1,:); % size LOS-by-6
    
end

function pdp = getSplitNLOSClusters(model,pdp)
    
    % named column subscripts
    [delay,power] = wireless.internal.channelmodels.namedDelayProfileColumns();
    
    % extract the NLOS clusters, sorted strongest to weakest
    % 'pdp' input has 'Lin' clusters, either from the CDL delay profile
    % definition or the custom delay profile
    % 'pdpNLOS' has Lin-LOS clusters, where LOS is 0 or 1
    pdpNLOS = getSortedNLOSClusters(model,pdp); % size (Lin-LOS)-by-6
    
    % extract the strongest (first) clusters and split them
    NSC = coder.ignoreConst(model.NumStrongestClusters);
    % The coder.ignoreConst on NSC is to ensure pdp is variable-size on the
    % first dimension.  This is required due to the multiple assignments
    % from values with varying sizes of the first dimension.
    pdp = pdpNLOS(1:NSC,:); % size NSC-by-6
    if (NSC)
        pdp = kron(pdp,[1; 1; 1]); % size (3*NSC)-by-6
        pdp(:,delay) = pdp(:,delay) + repmat([0; 1.28; 2.56]*model.ClusterDelaySpread,NSC,1);
        pdp(:,power) = pdp(:,power) + repmat(10*log10([10; 6; 4]/20),NSC,1);
    end
    
end

function pdp = getNLOSClusters(model,pdp)
    
    % extract the NLOS clusters, sorted strongest to weakest
    % 'pdp' input has 'Lin' clusters, either from the CDL delay profile
    % definition or the custom delay profile
    % 'pdpNLOS' has (Lin-LOS) clusters, where LOS is 0 or 1
    pdpNLOS = getSortedNLOSClusters(model,pdp); % size (Lin-LOS)-by-6
    
    % extract the clusters beyond the strongest (first) clusters that are
    % split
    NSC = model.NumStrongestClusters;
    pdp = pdpNLOS(NSC+1:end,:); % size (Lin-LOS-NSC)-by-6
    
end

function pdpNLOS = getSortedNLOSClusters(model,pdp)
    
    % named column subscripts
    [~,power] = wireless.internal.channelmodels.namedDelayProfileColumns();
    
    LOS = model.HasLOSCluster;
    pdpNLOS = pdp(1+LOS:end,:);
    if (model.NumStrongestClusters) % only sort if picking strongest clusters
        [~,idx]= sort(pdpNLOS(:,power),'descend');
        pdpNLOS = pdpNLOS(idx,:);
    end
    
end

function clusterTypes = getClusterTypes(delayProfileLOS,delayProfileSplitNLOS,delayProfileNLOS)

    nLOS = size(delayProfileLOS,1);
    nSplitNLOS = size(delayProfileSplitNLOS,1);
    nNLOS = size(delayProfileNLOS,1);
    c = cell(1,nLOS + nSplitNLOS + nNLOS);
    clusterTypes = coder.nullcopy(c);
    for i = 1:(nLOS + nSplitNLOS + nNLOS)
        if (i<=nLOS)
            clusterTypes{i} = 'LOS';
        elseif (i<=(nLOS + nSplitNLOS))
            clusterTypes{i} = 'SubclusteredNLOS';
        else
            clusterTypes{i} = 'NLOS';
        end
    end

end

function validateParameters(model)
    
    mfields = {'PathDelays','AveragePathGains',...
                'AnglesAoD','AnglesAoA','AnglesZoD','AnglesZoA',...
                'HasLOSCluster','NumStrongestClusters'};
    for f = 1:length(mfields)
        validateMandatoryParameter(model,mfields{f});
    end
    
end

% Validates the presence of a mandatory structure field parameter
function validateMandatoryParameter(model,parameter)

    if (~isfield(model,parameter))
        error('wireless:error','Parameter structure field ''%s'' not found',parameter);
    end
    
end