function [subframeBoundaries, numSubframes] = helperSubframeBoundaries(ampdu, phyConfig)
%helperSubframeBoundaries Return subframes information of an A-MPDU
%
%   This is an example helper function.
%
%   [SUBFRAMEBOUNDARIES, NUMSUBFRAMES] = helperSubframeBoundaries(AMPDU,
%   PHYCONFIG) finds the boundaries of each subframe in the given A-MPDU
%   frame, AMPDU, and outputs an array with 64 rows and 2 columns,
%   SUBFRAMEBOUNDARIES. Each row in the array contains two values: 1)
%   subframe start index and 2) length of the subframe. NUMSUBFRAMES
%   represents the number of subframes present in the AMPDU.
%
%   SUBFRAMEBOUNDARIES is a 64-by-2 array where each row is a pair of
%   (START_INDEX, SUBFRAME_LENGTH). START_INDEX is the index for the first
%   octet of the subframe. SUBFRAME_LENGTH is the length of the subframe in
%   octets.
%
%   NUMSUBFRAMES specifies the number of subframes in the given AMPDU.
%
%   AMPDU is the aggregated MPDU, specified as a column vector of decimal
%   octets.
%
%   PHYCONFIG is an object of type <a href="matlab:help('wlanHTConfig')">wlanHTConfig</a>, <a href="matlab:help('wlanVHTConfig')">wlanVHTConfig</a>, or
%   <a href="matlab:help('wlanHESUConfig')">wlanHESUConfig</a>.

%   Copyright 2019 The MathWorks, Inc.

%#codegen

% Initialization
subframeCount = 0;
subframeBoundaries = zeros(64, 2);
numSubframes = 0;
currByte = 1;
ampduLength = numel(ampdu);
mpduLength = 0;

while (currByte + 3) <= ampduLength
    % Search for delimiter: Each delimiter has 4th octet as the delimiter signature
    % 78 (0x4E). Refer Table 9-422 of IEEE Std 802.11-2016.
    if ampdu(currByte + 3) == 78
        startIdx = currByte;
        delimiterWithCRC = reshape(de2bi(ampdu(currByte : currByte+2), 8)', [], 1);
        
        % Validate delimiter CRC
        [delimiter, err] = checkDelimiterCRC(delimiterWithCRC);
        
        currByte = currByte + 4;
        % If this delimiter CRC fails, continue searching for another
        % delimiter.
        if ~err % Delimiter CRC passed
            % Extract the MPDU length from the delimiter
            if isa(phyConfig, 'wlanHTConfig')
                mpduLength = bi2deOptimized(delimiter(5:end));
            else % VHT or HE-SU or HE-EXT-SU
                mpduLength = bi2deOptimized([delimiter(5:end) delimiter(3:4)]);
            end
            
            % If zero delimiters are encountered, continue searching for a
            % non-zero delimiter.
            if (mpduLength == 0)
                continue;
            else
                numSubframes = numSubframes + 1;
                subframeBoundaries(numSubframes, :) = [startIdx, mpduLength+4];
            end
            
            % Stop processing a VHT/HE format A-MPDU if an EOF zero
            % delimiter is received
            if (isa(phyConfig, 'wlanVHTConfig') || isa(phyConfig, 'wlanHESUConfig')) && ...
                    (delimiter(1) == 1) && (mpduLength == 0)
                % EOF detected
                break;
            end
        end
        
        % Count the number of subframes in the A-MPDU
        subframeCount = subframeCount + 1;
        
        % Extract the MPDU bits based on the retrieved MPDU length
        if (currByte + mpduLength - 1) > ampduLength
            break;
        end
        currByte = currByte + mpduLength;
        
        % VHT single MPDU
        if (numSubframes == 1) && isa(phyConfig, 'wlanVHTConfig') && (delimiter(1) == 1)
            break;
        end
        
        % Skip subframe padding
        pad = abs(mod(mpduLength, -4));
        if pad
            currByte = currByte + pad;
        end
        
    else
        % Move on to find a valid delimiter
        currByte = currByte + 4;
    end
end

end

% Checks delimiter CRC
function [delimiter, err] = checkDelimiterCRC(delimiterWithCRC)
persistent detect

% 8-bit CRC Detector
if isempty(detect)
    detect = comm.CRCDetector([8 2 1 0], 'InitialConditions', 1, 'DirectMethod', true, 'FinalXOR', 1);
end

[delimiterColVector, err] = detect(double(delimiterWithCRC));
delimiter = reshape(delimiterColVector, 1, []);
end

% Optimized bi2de taken from comms.internal.utilities
function dec = bi2deOptimized(bin)
    dec = comm.internal.utilities.bi2deRightMSB(double(bin), 2);
end
