function macFrame = dcfGenerateMACFrame(frameType, retry, duration, addr1, addr2, payload, sequenceCounter)
%dcfGenerateMACFrame Generates MAC frames
%   MACFRAME = dcfGenerateMACFrame(FRAMETYPE, RETRY, DURATION, ADDR1,
%   ADDR2, PAYLOAD, SEQUENCECOUNTER) returns a MAC frame that includes the
%   given MAC header, payload and CRC. 
% 
%   MACFRAME is the output MAC frame, returned as an n-by-2 character
%   array, where each row represents an octet in hexadecimal format.
% 
%   FRAMETYPE is the type of frame, specified as one of 'DATA', 'ACK',
%   'RTS', or 'CTS'.
% 
%   RETRY is the retransmission flag, specified as true when the input
%   frame is being retransmitted.
% 
%   DURATION is the amount of time (in microseconds) being reserved on the
%   channel by this frame. Specify this value in the range of 0 to 32767.
%
%   ADDR1 is the receiver address field, specified as a 12-element
%   character or string scalar denoting a 6-octet hexadecimal value.
%
%   ADDR2 is the transmitter address field, specified as a 12-element
%   character or string scalar denoting a 6-octet hexadecimal value.
% 
%   PAYLOAD represents the MAC service data unit (MSDU), specified as an
%   integer column vector representing octets in decimal format. Each
%   element in the vector must be between 0 and 255 inclusive.

%   Copyright 2019 The MathWorks, Inc.

% Initialize address3 and sequence control to empty strings
addr3 = '';
seqControl = '';
% Set the default values for the different fields of 2nd byte of control
% field
toDS = 0;
fromDS = 0;
moreFrag = 0;
powerMgmt = 0;
moreData = 0;
protected = 0;
order = 0;
% Fill values based on the frame type
switch(frameType)
    case 'RTS'
        fc0 = 'B4';
        payloadHex = '';
    case 'CTS'
        fc0 = 'C4';
        addr2 = '';
        payloadHex = '';
    case 'ACK'
        fc0 = 'D4';
        addr2 = '';
        payloadHex = '';
    case 'Data'
        fc0 = '08';
        addr3 = '00123456789B';
        seqControl = dec2hex(sequenceCounter*16, 4);
        seqControl = [seqControl(3:4) seqControl(1:2)];
        payloadHex = reshape((dec2hex(payload, 2))', 1, []);
        fromDS = 1;
    otherwise
        error('Input frame type must be one of ''RTS'', ''CTS'', ''Ack'',''Data''.');
end

% Form 2nd byte of control field
fc1 = [toDS fromDS moreFrag retry powerMgmt moreData protected order];
% Convert decimal to hexadecimal
fc1 = dec2hex(bi2de(fc1), 2);
durationHex = dec2hex(duration, 4);
% Convert to little endian
durationHex = [durationHex(3:4) durationHex(1:2)];

% Prepend MAC header to payload
frameBytes = [fc0 fc1 durationHex addr1 addr2 addr3 seqControl payloadHex];
% Append CRC
frameBits = appendFCS(frameBytes);
macFrame = dec2hex(bi2de(reshape(frameBits, 8, [])'), 2);
end

% appendFCS Calculates and Appends CRC to the current frame
function codedframe = appendFCS(frame)
persistent gen
if isempty(gen)
    gen = comm.CRCGenerator([32 26 23 22 16 12 11 10 8 7 5 4 2 1 0],...
        'InitialConditions', 1, 'DirectMethod', true, 'FinalXOR', 1);
end

% 1 octet = 8 bits
octetLength = 8;

% Convert octets to bits to add FCS
frame = reshape(frame, 2, [])';
frameBits = de2bi(hex2dec(frame), octetLength);
frameBitsColVector = reshape(frameBits', numel(frameBits), 1);

% Append FCS to the frame bits
codedframe = gen(double(frameBitsColVector));
end
