function radiotapBytes = createRadiotapHeader(frameFormat, mcs, channelBandwidth)
%createRadiotapHeader Create a radiotap header
%
%   RADIOTAPBYTES = createRadiotapHeader(FRAMEFORMAT, MCS, CHANNELBANDWIDTH)
%   creates radiotap header with the given frame format, MCS, and channel
%   bandwidth
%
%   FRAMEFORMAT       - Frame format is a char vector.The value of frame
%                       format can be 'Non-HT', 'HT-Mixed', 'VHT', 'HE-SU',
%                       'HE-EXT-SU'.
%   MCS               - Modulation coding scheme.
%   CHANNELBANDWIDTH  - Channel bandwidth.

%   Copyright 2019 The MathWorks, Inc.

%#codegen

    % Frame format as a character array
    frameFormat = char(frameFormat);
    % Default length of radiotap
    defaultRadiotapLen = 8;
    % Radiotap version
    radiotapVersion = 0;
    % Padding
    padding = 0;
    % Radiotap flags. Set the flag to indicate FCS is available.
    radiotapFlags = 16;

    % Set bandwidth
    switch channelBandwidth
        case 20
            bandwidth = 0;
        case 40
            bandwidth = 1;
        case 80
            bandwidth = 2;
        case 160
            bandwidth = 3;
        otherwise
            % For codegen
            bandwidth = 0; %#ok<NASGU>
            error(['Invalid value of channel bandwidth ' char(string(channelBandwidth)) ...
                '. Valid values are 20, 40, 80, 160']);
    end

    validateattributes(mcs, {'numeric'}, {'scalar'}, mfilename, 'MCS');
    % Form the MCS data. The value 3 represents presence of bandwidth and
    % MCS information.
    mcsData = [3; bandwidth; mcs];

    switch frameFormat
        % Add radiotap header if frame format is of type Non-HT
        case 'Non-HT'
            % Set the flags for radiotap information. The value 6
            % represents presence of data rate and radiotap flags.
            presentFlags = [6; 0; 0; 0];
            % Data rate set
            dataRateSet = [6 9 12 18 24 36 48 54];
            % Get dataRate from dataRateSet
            dataRate = dataRateSet(mcs+1);
            % Form 'Non-HT' data
            radiotapDataNonHT = [radiotapFlags; dataRate*2];
            % Prepare radiotap header
            radiotapBytes = prepareRadiotapHeader(radiotapVersion, padding, ...
                presentFlags, radiotapDataNonHT, defaultRadiotapLen);

        % Add radiotap header if frame format is of type VHT
        case 'VHT'
            % Set the flags for radiotap information. The value 2
            % represents presence radiotap flag. The value 40
            % represents presence of MCS, VHT data.
            presentFlags = [2; 0; 40; 0];
            % Form 'VHT' data
            radiotapVHT = [2; 192; 0; bandwidth; 0; 0; 63; 1; 19];
            radiotapDataVHT = [radiotapFlags; mcsData; radiotapVHT];
            % Prepare radiotap header
            radiotapBytes = prepareRadiotapHeader(radiotapVersion, padding, ...
                presentFlags, radiotapDataVHT, defaultRadiotapLen);

        % Add radiotap header if frame format is of type HT-Mixed
        case 'HT-Mixed'
            % Set the flags for radiotap information. The value 2
            % represents presence radiotap flag. The value 8 represents
            % presence of MCS and HTMixed data.
            presentFlags = [2; 0; 8; 0];
            % Form 'HT-Mixed' data
            radiotapDataHTMixed  = [radiotapFlags; mcsData];
            % Prepare radiotap header
            radiotapBytes = prepareRadiotapHeader(radiotapVersion, padding, ...
                presentFlags, radiotapDataHTMixed, defaultRadiotapLen);

        % Add radiotap header if frame format is of type HE_SU
        case 'HE-SU'
            % Set the flags for radiotap information. The value 2
            % represents presence radiotap flag. The value 136
            % represents presence of MCS and HE_SU data.
            presentFlags = [2; 0; 136; 0];
            % Form 'HE-SU' data
            radiotapHE = [134; 228; 0; 70; 32; 0; 0; 0; 0; bandwidth; 127; 0];
            radiotapDataHE_SU= [radiotapFlags; mcsData; radiotapHE];
            % Prepare radiotap header
            radiotapBytes = prepareRadiotapHeader(radiotapVersion, padding, ...
                presentFlags, radiotapDataHE_SU, defaultRadiotapLen);

        % Add radiotap header if frame format is of type HE-EXT-SU
        case 'HE-EXT-SU'
            % Set the flags for radiotap information. The value 2
            % represents presence radiotap flag. The value 136
            % represents presence of MCS and HE_EXT_SU data.
            presentFlags = [2; 0; 136; 0];
            % Form 'HE-EXT-SU' data
            radiotapHE = [150; 228; 0; 70; 32; 0; 0; 0; 0; bandwidth; 127; 0];
            radiotapDataHE_EXT_SU= [radiotapFlags; mcsData; radiotapHE];
            % Prepare radiotap header
            radiotapBytes = prepareRadiotapHeader(radiotapVersion, padding, ...
                presentFlags, radiotapDataHE_EXT_SU, defaultRadiotapLen);

        % Add radiotap header if frame format is of type HE-MU-OFDMA
        case 'HE-MU-OFDMA'
            presentFlags = [0; 0; 0; 0];
            radiotapHE_MU_OFDMA = [0; 0; 0; 0; 0; 0];
            radiotapBytes = prepareRadiotapHeader(radiotapVersion, padding, ...
                presentFlags, radiotapHE_MU_OFDMA, defaultRadiotapLen);

        otherwise
            % For codegen
            radiotapBytes = zeros(0, 1); %#ok<NASGU>
            error(['Invalid value of frameFormat ' frameFormat ...
                '. Valid values are Non-HT, HT-Mixed, HE-SU, HE-EXT-SU']);
    end
end

function radioTapHeader = prepareRadiotapHeader(radiotapVersion, padding, ...
                 presentFlags, radiotapData, defaultRadiotapLen)
%prepareRadiotapHeader Prepare radiotap header for a given radiotap data

    % Calculate the radiotap length
    radiotapLen = defaultRadiotapLen + numel(radiotapData);
    radiotapLenPad = radiotapLen+mod(4 - mod(radiotapLen, 4), 4);
    % Calculate the header length
    headerLen = [mod(radiotapLenPad,256); fix(radiotapLenPad/256)];
    % Form the radiotap header
    radioTapHeader = [radiotapVersion; padding; headerLen; presentFlags; ...
        radiotapData; zeros(mod(4 - mod(radiotapLen, 4), 4),1)];
end