function waveformStruct = evdoReverseReferenceChannels(varargin)
%evdoReverseReferenceChannels Preset reverse 1xEV-DO waveforms
%   WAVEFORMSTRUCT = evdoReverseReferenceChannels(WAVEFORMID) returns a
%   configuration structure WAVEFORMSTRUCT that can be passed to
%   evdoReverseWaveformGenerator in order to generate the corresponding
%   waveform. evdoReverseReferenceChannels generates configuration
%   structures for Release 0 and Revision A waveforms.
%   
%   WAVEFORMID can take one of the following string values:
%
%   Release 0 options - 'Rel0-DataRate(bps)' format: 
%   'Rel0-9600',  'Rel0-19200',  'Rel0-38400',  'Rel0-76800',  'Rel0-153600'
% 
%   Revision A options - 'RevA-PayloadSize(bits)-NumSlots' format: 
%   'RevA-128-4',   'RevA-128-8',   'RevA-128-12',   'RevA-128-16',
%   'RevA-256-4',   'RevA-256-8',   'RevA-256-12',   'RevA-256-16',
%   'RevA-512-4',   'RevA-512-8',   'RevA-512-12',   'RevA-512-16',
%   'RevA-768-4',   'RevA-768-8',   'RevA-768-12',   'RevA-768-16',
%   'RevA-1024-4',  'RevA-1024-8',  'RevA-1024-12',  'RevA-1024-16',
%   'RevA-1536-4',  'RevA-1536-8',  'RevA-1536-12',  'RevA-1536-16',
%   'RevA-2048-4',  'RevA-2048-8',  'RevA-2048-12',  'RevA-2048-16',
%   'RevA-3072-4',  'RevA-3072-8',  'RevA-3072-12',  'RevA-3072-16',
%   'RevA-4096-4',  'RevA-4096-8',  'RevA-4096-12',  'RevA-4096-16',
%   'RevA-6144-4',  'RevA-6144-8',  'RevA-6144-12',  'RevA-6144-16',
%   'RevA-8192-4',  'RevA-8192-8',  'RevA-8192-12',  'RevA-8192-16',
%   'RevA-12288-4', 'RevA-12288-8', 'RevA-12288-12', 'RevA-12288-16'
%
% 
%   WAVEFORMSTRUCT = evdoReverseReferenceChannels(WAVEFORMID, NUMPACKETS)
%   behaves as evdoReverseReferenceChannels(WAVEFORMID) but creates
%   NUMPACKETS packets with identical parameters. If omitted, NUMPACKETS
%   defaults to 1.
%
%   Example 1:
%      config   = evdoReverseReferenceChannels('Rel0-38400', 100);
%      waveform = evdoReverseWaveformGenerator(config);
%
%   Example 2:
%      config   = evdoReverseReferenceChannels('RevA-1024-12', 100);
%      waveform = evdoReverseWaveformGenerator(config);
% 
%   See also evdoReverseWaveformGenerator, evdoForwardReferenceChannels,
%            cdma2000ReverseReferenceChannels, cdma2000ForwardReferenceChannels

%   Copyright 2015 The MathWorks, Inc.

    % default values:
    if nargin > 0
        [varargin{:}] = convertStringsToChars(varargin{:});
    end
    
    waveformID = 'RevA-128-4'; 
    numPackets = 1;

    if nargin >= 1 
        % 1st argument validation:
        validateattributes(varargin{1}, {'char'}, {'row'}, 'evdoReverseReferenceChannels', 'waveformID');
        waveformID = varargin{1};
        
        if nargin == 2 % numPackets specified
            % 2nd argument validation:
            validateattributes(varargin{2}, {'numeric'}, {'scalar', 'real', 'integer', 'positive'}, 'evdoReverseReferenceChannels', 'numPackets');
            numPackets = varargin{2};
        elseif nargin >=3
            id = 'comm:cdma2000evdoParameterGeneration:TooManyInputArgs';
            error(id, getString( message(id, 'evdoReverseReferenceChannels', 2) ) );
        end
    end
    
    if strncmpi(waveformID, 'Rel0-', 5) % Release 0 waveforms
        
        isRel0 = true;
        
        % break down the info from the ID:
        [dataRate] = parseWaveformID(waveformID, isRel0);

        % throw an error if the combination is not specified in the standard
        % or if a 2nd number is specified in the id
        validateRelease0Specification(dataRate);

        % return preset structure:
        waveformStruct = createRelease0Configuration(dataRate, numPackets);
        
    elseif strncmpi(waveformID, 'RevA-', 5) % Revision A waveforms
        
        isRel0 = false;
        
        % break down the info from the ID:
        [payloadSize, numSlots] = parseWaveformID(waveformID, isRel0);
        
        % throw an error if the combination is not specified in the standard
        validateRevisionASpecification(payloadSize, numSlots);
        
        % return preset structure:
        waveformStruct = createRevisionAConfiguration(payloadSize, numSlots, numPackets);
        
    else % Invalid input ID, message points to all acceptable IDs
        invalidIDerror(waveformID);
    end
end
    
function [number1, number2] = parseWaveformID(waveformID, isRel0)
    
    % 'Rel0-DataRate' format, e.g., 'Rel0-38400'
    % or
    % 'RevA-PayloadSize-NumSlots' format, e.g., 'RevA-1536-12'

    delimiter = '-';
    
    % get first substring until delimiter ('-')
    [~, remain] = strtok(waveformID, delimiter);
    
    % discard the first token which is the 'Rel0-' prefix (existence confirmed by calling function)    
    % get second substring until next delimiter ('-') = DataRate or PayloadSize
    [token, remain] = strtok(remain, delimiter);
    
    % parse the first number:
    if isempty(token) || isnan(str2double(token))
        % Invalid input ID, hint all that are acceptable:
        invalidIDerror(waveformID);
    end
    
    number1 = str2double(token);
    
    if (isRel0 && ~isempty(remain)) || (~isRel0 && isempty(remain))
        invalidIDerror(waveformID);
    end
    
    % parse a 2nd optional number (Revision A scenario, NumSlots)
    if ~isRel0 % remain will not be empty because of check above
        
        [token, ~] = strtok(remain, delimiter);
        
        if isnan(str2double(token))
            invalidIDerror(waveformID);
        end
        number2 = str2double(token);
    end
end

function validateRelease0Specification(dataRate)

    % rates specified in the standard
    validRates = [ 9600 19200 38400 76800 153600 ];
    
    if ~any( ismember(validRates, dataRate) )
        
        waveformID = ['Rel0-' num2str(dataRate)];
        invalidIDerror(waveformID);
    end
end

function validateRevisionASpecification(payloadSize, numSlots)

    % possible values for number of slots specified in the standard:
    validNumSlots = [4 8 12 16];
    
    % possible values for payload size specified in the standard:
    validPayloadSizes = [128 256 512 768 1024 1536 2048 3072 4096 6144 8192 12288];
    
    % the combinations are exhaustive: validPayloadSizes x validNumSlots,
    % so we can check values separately, no need for a joint 12x4 matrix

    if ~any(ismember(validNumSlots, numSlots)) || ~any(ismember(validPayloadSizes, payloadSize))
        waveformID = ['RevA-' num2str(payloadSize) '-' num2str(numSlots)];
        invalidIDerror(waveformID);
    end
end

function waveformStruct = createRelease0Configuration(dataRate, numPackets)

    % top-level waveform configuration:
    waveformStruct = struct();
    waveformStruct.Release = 'Release0'; % show Revision on top
    waveformStruct = addCommonWaveformProperties(waveformStruct);
    % According to 12.2.1.3.1.1-1 in C.S0024 A_v3.0, 
    % Chips/Bit * Bits/Packet = 32768 for all data rates:
    waveformStruct.NumChips = numPackets * 32768;

    % create packets with the DataRate specification:
    singlePacket.Power          = 0;
    singlePacket.DataSource     = {'PN9', 1};
    singlePacket.EnableCoding   = 'On';
    singlePacket.DataRate       = dataRate;
    waveformStruct.PacketSequence = repmat(singlePacket, 1, numPackets);
end

function waveformStruct = createRevisionAConfiguration(payloadSize, numSlots, numPackets)

    % top-level waveform configuration:
    waveformStruct.Release = 'RevisionA'; % show Revision on top
    waveformStruct = addCommonWaveformProperties(waveformStruct);
    % according to 12.2.1.3.1 in C.S0024 A_v3.0, each slot has 2048 chips.
    waveformStruct.NumChips = numPackets * numSlots * 2048;
    
    % create packets with the PayloadSize-NumSlots specification:
    singlePacket = struct();
    singlePacket.Power          = 0;
    singlePacket.DataSource     = {'PN9', 1};
    singlePacket.EnableCoding   = 'On';
    singlePacket.PayloadSize    = payloadSize;
    singlePacket.NumSlots       = numSlots;
    singlePacket.DataRate       = getRevisionADataRate(payloadSize, numSlots);
    waveformStruct.PacketSequence = repmat(singlePacket, 1, numPackets);
    
    % add a disabled Auxiliary Pilot Channel:
    auxPilotChannel.Enable     = 'Off';
    auxPilotChannel.Power      = 0;
    auxPilotChannel.DataSource = {'PN9', 1};
    waveformStruct.AuxPilotChannel = auxPilotChannel;
end

function dataRate = getRevisionADataRate(payloadSize, numSlots)

    %                  PayloadSize (bits)    NumSlots     DataRate (bps)
    revisionAtable = [      128                 4           19200;
                            128                 8           9600;
                            128                 12          6400;
                            128                 16          4800;
                            256                 4           38400;
                            256                 8           19200;
                            256                 12          12800;
                            256                 16          9600;
                            512                 4           76800;
                            512                 8           38400;
                            512                 12          25600;
                            512                 16          19200;
                            768                 4           115200;
                            768                 8           57600;
                            768                 12          38400;
                            768                 16          28800;
                            1024                4           153600;
                            1024                8           76800;
                            1024                12          51200;
                            1024                16          38400;
                            1536                4           230400;
                            1536                8           115200;
                            1536                12          76800;
                            1536                16          57600;
                            2048                4           307200;
                            2048                8           153600;
                            2048                12          102400;
                            2048                16          76800;
                            3072                4           460800;
                            3072                8           230400;
                            3072                12          153600;
                            3072                16          115200;
                            4096                4           614400;
                            4096                8           307200;
                            4096                12          204800;
                            4096                16          153600;
                            6144                4           921600;
                            6144                8           460800;
                            6144                12          307200;
                            6144                16          230400;
                            8192                4           1228800;
                            8192                8           614400;
                            8192                12          409600;
                            8192                16          307200;
                            12288               4           1843200;
                            12288               8           921600;
                            12288               12          614400;
                            12288               16          460800];

    foundRow = ismember(revisionAtable(:, 1:2), [payloadSize numSlots], 'rows');
    dataRate = revisionAtable(foundRow, 3);
end

function waveformStruct = addCommonWaveformProperties(waveformStruct)
    
    % properties common between Release 0 and Revision A
        
    waveformStruct.LongCodeMaskI = 0;
    waveformStruct.LongCodeMaskQ = 0;
    
    % Filtering and modulation parameters:
    waveformStruct.OversamplingRatio    = 4; 
    waveformStruct.FilterType           = 'cdma2000Long';
    waveformStruct.InvertQ              = 'Off';
    waveformStruct.EnableModulation     = 'Off';
    waveformStruct.ModulationFrequency  = 0;
    
    % add a Pilot Channel:
    pilotChannel.Enable         = 'On';
    pilotChannel.Power          = 0;
    pilotChannel.DataSource     = {'PN9', 1};
    pilotChannel.EnableCoding   = 'On';
    waveformStruct.PilotChannel = pilotChannel;
    
    % add a disabled ACK Channel:
    ackChannel.Enable     = 'Off';
    ackChannel.Power      = 0;
    ackChannel.DataSource = {'PN9', 1};
    waveformStruct.ACKChannel = ackChannel;
end

function invalidIDerror(waveformID)
    invalidID       = 'comm:cdma2000evdoParameterGeneration:InvalidID';
    thisFunction    = 'evdoReverseReferenceChannels';
    error(invalidID, getString( message(invalidID, waveformID, thisFunction, thisFunction)));
end

