%hPlotDetectedRNTIs Plot the detected RNTI values
%   SUMMARY=hPlotDetectedRNTIs(RESULTS,ENB,DLRXGRID) plots detected RNTIs
%   given results table RESULTS, cell-wide settings structure ENB and
%   received downlink resource grid DLRXGRID. The received downlink
%   resource grid is displayed and the RNTIs are used to label the PDSCH
%   resources signaled by the corresponding DCI messages. An empty uplink
%   resource grid is also displayed and the PUSCH resources signaled by any
%   DCI format 0 or format 4 messages are displayed and labeled with the
%   corresponding RNTIs. A summary table of the resources for each DCI
%   message is returned in SUMMARY.

%   Copyright 2017-2018 The MathWorks, Inc.

function summary = hPlotDetectedRNTIs(results,enb,dlrxgrid,varargin)

    if (nargin>3)
        ulrxgrid = varargin{1};
    else
        ulrxgrid = zeros(size(dlrxgrid));
    end
    
    summary = table;
    
    % Get list of unique RNTIs, in order of occurrence
    detectedRNTIs = results.DetectedRNTI;
    uniqueRNTIs = unique(detectedRNTIs,'stable');

    % Sort unique RNTIs in order of frequency of occurrence (to optimize
    % choice of colors in plotting)
    occurrences = sum(detectedRNTIs == uniqueRNTIs.');
    [~,i] = sort(occurrences,'descend');
    uniqueRNTIs = uniqueRNTIs(i);

    % Create plot for OFDM resource grids
    f = figure;
    p = f.Position;
    f.Position(1) = p(1) - p(3)*0.25;
    f.Position(3) = p(3) * 1.5;
    f.InvertHardcopy = 'off';
    gridDims = lteDLResourceGridSize(enb);
    L = gridDims(2);
    nSubframes = size(dlrxgrid,2) / L;
    SFNs = enb.NFrame + (enb.NSubframe + (0:(nSubframes-1)))/10;
    subplotSetup(1,dlrxgrid,SFNs,'Downlink PDSCH');
    subplotSetup(2,ulrxgrid,SFNs,'Uplink PUSCH');
    if (nargin==3)
        caxis([0 1]);
    end
    colors = [0.13 0.57 0.86; 0.88 0.35 0.13; 0.93 0.69 0.13; 0.66 0.35 0.73; 0.47 0.67 0.19];
    colors = [colors; (colors+2)/3; colors*2/3; (colors/2+2)/3];
    nc = size(colors,1);
    ncrep = ceil(numel(uniqueRNTIs)/nc);
    colors = repmat(colors,ncrep,1);
    
    % Add labels and boundaries for each detected RNTI
    allCFIs = enb.CFI;
    for i = 0:(nSubframes-1)
        
        % Set the CFI that was decoded for this subframe, this affects the
        % PDSCH resources
        enb.CFI = allCFIs(mod(i,length(allCFIs)) + 1);
        
        % Extract the results for this subframe
        subframeResults = results(results.Subframe==i,:);
        
        % 1 = plot patches, 2 = plot boundaries, 3 = add RNTI labels
        for loop = 1:3 
            
            % For each result
            extent = zeros(height(subframeResults),2);
            for j = 1:height(subframeResults)

                % Extract the RNTI and DCI message
                result = subframeResults(j,:);
                RNTI = result.DetectedRNTI;
                dci = result.DCI{1};

                % Compute the subscript indices for the PDSCH or PUSCH
                % allocation
                uplink = any(strcmpi(dci.DCIFormat,{'Format0','Format4'}));
                subplot(2,1,1 + uplink);
                [ind,prbset] = allocationIndices(enb,dci);
                [k,l,p] = ind2sub(gridDims,ind);
                k = k(p==1);
                l = l(p==1);
                l = l + i*L;
                color = colors(uniqueRNTIs == RNTI,:);

                if (loop==1)
                    
                    % Create entry in summary output for this result
                    summaryEntry = struct();
                    summaryEntry.SFN = enb.NFrame + (enb.NSubframe/10);
                    summaryEntry.RNTI = RNTI;
                    summaryEntry.NumErrors = result.NumErrors;
                    summaryEntry.DCIFormat = result.DCIFormat;
                    if (uplink)
                        summaryEntry.LinkDirection = "Uplink";
                        sfgrid = ulrxgrid(:,(1:L) + result.Subframe*L);
                    else
                        summaryEntry.LinkDirection = "Downlink";
                        sfgrid = dlrxgrid(:,(1:L) + result.Subframe*L);
                    end
                    summaryEntry.PRBSet = "[" + ellipsify(prbset) + "]";
                    symbols = lteExtractResources(ind,sfgrid);
                    power = round(10*log10(var(symbols)),2);
                    summaryEntry.Power = power;
                    summaryEntry = struct2table(summaryEntry);
                    summary = [summary; summaryEntry]; %#ok<AGROW>
                    
                    % For each allocated PRB, shade the allocated region
                    % (given by the convex hull of the resource elements in
                    % that PRB)
                    for prb = prbset.'
                        thisprb = (floor((k-1)/12) == prb);
                        kprb = k(thisprb);
                        lprb = l(thisprb);
                        ch = convhull(kprb,lprb);
                        pt = patch(lineoutside(lprb(ch),0.5),lineoutside(kprb(ch),0.5),color);
                        pt.FaceAlpha = 0.3;
                        pt.LineStyle = 'none';
                    end
                    
                elseif (loop==2 && ~isempty(prbset))
                    
                    % Plot the boundary of the PDSCH or PUSCH allocation
                    % (given by the convex hull of all resource elements).
                    % In the case that this allocation's extent in
                    % frequency overlaps with another in the same subframe,
                    % adjust the width of the boundary so that they don't
                    % overlap
                    ch = convhull(k,l);
                    extent(j,:) = [min(k(ch)) max(k(ch))];
                    if (uplink)
                        extent(j,:) = -extent(j,:);
                    end
                    adj = 0;
                    for e = 1:j-1
                        if (~isempty(intersect(extent(j,1):extent(j,2),extent(e,1):extent(e,2))))
                            adj = adj + 1;
                        end
                    end
                    line(lineoutside(l(ch),0.4 - adj*0.2),k(ch),'Color',color);
                    
                elseif (~isempty(prbset))
                    
                    % Add RNTI label
                    tx = mean(l);
                    ty = mean(k);
                    rntiLabel = RNTI;
                    if (result.NumErrors)
                        rntiLabel = rntiLabel + "?";
                    end
                    lowerquarter = char(hex2dec('2582'));
                    fullblock = char(hex2dec('2587'));
                    for ll = 1:2
                        if (ll==1)
                            block = fullblock;
                            thiscolor = [0.2 0.2 0.2];
                            fontsize = 8;
                        else
                            block = lowerquarter;
                            thiscolor = color;
                            fontsize = 7;
                        end
                        t = text(tx,ty,repmat(block,1,3));
                        t.FontSize = fontsize;
                        t.FontSmoothing = 'off';
                        t.HorizontalAlignment = 'center';
                        t.Color = thiscolor;
                    end
                    t = text(tx,ty,rntiLabel);
                    t.FontSize = 7;
                    t.HorizontalAlignment = 'center';
                    t.FontWeight = 'bold';
                    t.Color = [1 1 1];
                    
                end

            end
            
        end
        
        % Update the subframe number and frame number
        enb.NSubframe = mod(enb.NSubframe + 1,10);
        if (enb.NSubframe==0)
            enb.NFrame = mod(enb.NFrame + 1,1024);
        end
    
    end

end

function [ind,prbset] = allocationIndices(enb,dci)
    
    prbset = lteDCIResourceAllocation(enb,dci);

    if(any(strcmpi(dci.DCIFormat,{'Format0','Format4'})))
    
        ue = rmfield(enb,'NDLRB');
        ue.NULRB = enb.NDLRB;
        ue.CyclicPrefixUL = enb.CyclicPrefix;
        ue.Shortened = 0;
        ue.NTxAnts = 1;
        ind = double(ltePUSCHIndices(ue,struct('PRBSet',prbset)));
        
    else
        
        prbset = lteDCIResourceAllocation(enb,dci);
        ind = double(ltePDSCHIndices(enb,struct(),prbset));
        
    end
    
end

function subplotSetup(n,grid,SFNs,link)

    subplot(2,1,n);
    gridpower = 20*log10(abs(grid));
    im = imagesc(gridpower);
    colormap('gray');
    if (isfinite(max(gridpower(:))))
        caxis(max(gridpower(:)) + [-40 0]);
    end
    im.Parent.Color = [0 0 0];
    axis xy;
    axis([-1 size(grid,2)+2 -5 size(grid,1)+6]);
    NRB = size(grid,1) / 12;
    D = ceil(NRB / 8);
    rbticks = 0:D:(NRB-1);
    yticks(6 + rbticks*12);
    yticklabels(strsplit(num2str(rbticks)));
    nSubframes = length(SFNs);
    L = floor(size(grid,2)/nSubframes);
    ylabel('PRB number');
    xticks(L/2 + 0.5 + (0:(nSubframes-1))*L);
    xticklabels(makeSFNlabels(SFNs));
    xlabel('System Frame Number (SFN)');
    title([link ' resources allocated by DCI messages with indicated RNTI']);
    
end

function x = lineoutside(x,a)

    x(x==max(x)) = x(x==max(x)) + a;
    x(x==min(x)) = x(x==min(x)) - a;
    
end

function labels = makeSFNlabels(SFNs)

    labels = "." + string(strsplit(num2str(mod(SFNs*10,10))));
    firsts = logical([1 diff(floor(SFNs))]);
    
    if(length(SFNs)>20)
        labels(firsts) = string(strsplit(num2str(floor(SFNs(firsts)))));
        labels(~firsts) = "";
    else
        labels(firsts) = string(strsplit(num2str(floor(SFNs(firsts))))) + labels(firsts);
    end

end

function prbstring = ellipsify(prbset)

    d = [0 diff(prbset.')];
    d(find(d>1) - 1) = 0;
    d(end) = 0;
    d = (d==1);

    prbstring = "";
    for i = 1:length(prbset)
        if (~d(i))
            if (i>1 && ~d(i-1))
                prbstring = prbstring + " ";
            end
            prbstring = prbstring + num2str(prbset(i));
        else
            if (~d(i-1) && d(i))
                prbstring = prbstring + "...";
            end
        end
    end
    
end
