function waveformStruct = evdoForwardReferenceChannels(varargin)
%evdoForwardReferenceChannels Preset forward 1xEV-DO waveforms
%   WAVEFORMSTRUCT = evdoForwardReferenceChannels(WAVEFORMID) returns a
%   configuration structure WAVEFORMSTRUCT that can be passed to
%   evdoForwardWaveformGenerator in order to generate the corresponding
%   waveform. evdoForwardReferenceChannels 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)-NumSlots format':
%   'Rel0-38400-16',   'Rel0-76800-8',     'Rel0-153600-4',     'Rel0-307200-2',
%   'Rel0-307200-4',   'Rel0-614400-1',    'Rel0-614400-2',     'Rel0-921600-2',
%   'Rel0-1228800-1',  'Rel0-1228800-2',   'Rel0-1843200-1',    'Rel0-2457600-1'
%
%   Revision A options - 'RevA-PacketSize(bits)-NumSlots-PreambleLength(chips)' format:
%   'RevA-128-1-64',   'RevA-128-2-128',   'RevA-128-4-256',    'RevA-128-4-1024',
%   'RevA-128-8-512',  'RevA-128-16-1024', 'RevA-256-1-64',     'RevA-256-2-128',
%   'RevA-256-4-256',  'RevA-256-4-1024',  'RevA-256-8-512',    'RevA-256-16-1024',
%   'RevA-512-1-64',   'RevA-512-2-64',    'RevA-512-2-128',    'RevA-512-4-128',
%   'RevA-512-4-256',  'RevA-512-4-1024',  'RevA-512-8-512',    'RevA-512-16-1024',
%   'RevA-1024-1-64',  'RevA-1024-2-64',   'RevA-1024-2-128',   'RevA-1024-4-128',
%   'RevA-1024-4-256', 'RevA-1024-8-512',  'RevA-1024-16-1024', 'RevA-2048-1-64',
%   'RevA-2048-2-64',  'RevA-2048-4-128',  'RevA-3072-1-64',    'RevA-3072-2-64',
%   'RevA-4096-1-64',  'RevA-4096-2-64',   'RevA-5120-1-64',    'RevA-5120-2-64'
%
%    
%   WAVEFORMSTRUCT = evdoForwardReferenceChannels(WAVEFORMID, NUMPACKETS)
%   behaves as evdoForwardReferenceChannels(WAVEFORMID) but creates
%   NUMPACKETS packets with identical parameters. If omitted, NUMPACKETS
%   defaults to 1.
%
%   Example 1:
%      config   = evdoForwardReferenceChannels('Rel0-307200-4', 100);
%      waveform = evdoForwardWaveformGenerator(config);
%
%   Example 2:
%      config   = evdoForwardReferenceChannels('RevA-5120-2-64', 100);
%      waveform = evdoForwardWaveformGenerator(config);
% 
%   See also evdoForwardWaveformGenerator, evdoReverseReferenceChannels,
%            cdma2000ForwardReferenceChannels, cdma2000ReverseReferenceChannels

%   Copyright 2015 The MathWorks, Inc.

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

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

        % throw an error if the combination is not specified in the standard
        % or if a 3rd number is specified in the id
        validateRelease0Specification(dataRate, numSlots);
        
        % return preset structure:
        waveformStruct = createRelease0Configuration(dataRate, numSlots, numPackets);
        
    elseif strncmpi(waveformID, 'RevA-', 5) % Revision A waveforms
        
        isRel0 = false;
        
        % break down the info from the ID:
        [packetSize, numSlots, preambleLength] = parseWaveformID(waveformID, isRel0);
        
        % throw an error if the combination is not specified in the standard
        validateRevisionASpecification(packetSize, numSlots, preambleLength);
        
        % return preset structure:
        waveformStruct = createRevisionAConfiguration(packetSize, numSlots, preambleLength, numPackets);
        
    else % Invalid input ID, message points to all acceptable IDs
        invalidIDerror(waveformID);
    end
end

function [number1, number2, number3] = parseWaveformID(waveformID, isRel0)
    
    % 'Rel0-DataRate-NumSlots' format, e.g., 'Rel0-38400-16'
    % or
    % 'RevA-PacketSize-NumSlots-PreambleLength' format, e.g., 'RevA-512-2-64'

    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 PacketSize
    [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);
    
    % now parse the 2nd number (always number of slots), so get third substring
    [token, remain] = strtok(remain, delimiter);
    
    if isempty(token) || isnan(str2double(token))
        invalidIDerror(waveformID);
    end
    
    number2 = str2double(token);
    
    if (isRel0 && ~isempty(remain)) || (~isRel0 && isempty(remain))
        invalidIDerror(waveformID);
    end
    
    % parse a 3rd optional number (Revision A scenario)
    if ~isRel0 % remain will not be empty because of check above
        
        [token, ~] = strtok(remain, delimiter);
        
        if isnan(str2double(token))
            invalidIDerror(waveformID);
        end
        number3 = str2double(token);
    end
end

function validateRelease0Specification(dataRate, numSlots)
    
    % the following matrix contains the valid combinations.
    %                    DataRate     NumSlots
    validCombinations = [ 38400         16; ...
                          76800         8;  ...
                          153600        4; ...
                          307200        2; ...
                          307200        4; ...
                          614400        1; ...
                          614400        2; ...
                          921600        2; ...
                          1228800       1; ...
                          1228800       2; ...
                          1843200       1; ...
                          2457600       1];
    
    if ~any( ismember(validCombinations, [dataRate numSlots], 'rows') )
        waveformID = ['Rel0-' num2str(dataRate) '-' num2str(numSlots)];
        invalidIDerror(waveformID);
    end
end

function validateRevisionASpecification(packetSize, numSlots, preambleLength)        
    
    % the following matrix contains the valid combinations.
    %                  PacketSize    NumSlots     PreambleLength
    validCombinations = [ 128           1               64 ; ...
                          128           2               128; ...
                          128           4               256; ...
                          128           4               1024; ...
                          128           8               512; ...
                          128           16              1024; ...
                          256           1               64; ...
                          256           2               128; ...
                          256           4               256; ...
                          256           4               1024; ...
                          256           8               512; ...
                          256           16              1024; ...
                          512           1               64; ...
                          512           2               64; ...
                          512           2               128; ...
                          512           4               128; ...
                          512           4               256; ...
                          512           4               1024; ...
                          512           8               512; ...
                          512           16              1024; ...
                          1024          1               64; ...
                          1024          2               64; ...
                          1024          2               128; ...
                          1024          4               128; ...                          
                          1024          4               256; ...
                          1024          8               512; ...
                          1024          16              1024; ...
                          2048          1               64; ...
                          2048          2               64; ...
                          2048          4               128; ...
                          3072          1               64; ...
                          3072          2               64; ...
                          4096          1               64; ...
                          4096          2               64; ...
                          5120          1               64; ...
                          5120          2               64  ];
    
    if ~any( ismember(validCombinations, [packetSize numSlots preambleLength], 'rows') )
        waveformID = ['RevA-' num2str(packetSize) '-' num2str(numSlots) '-' num2str(preambleLength)];
        invalidIDerror(waveformID);
    end
end

function waveformStruct = createRelease0Configuration(dataRate, numSlots, numPackets)

    % top-level waveform configuration:
    waveformStruct.Release = 'Release0';  % show Revision on top
    waveformStruct = addCommonWaveformProperties(waveformStruct);
    % according to 9.3.1.3.1 of C.S0024-0 v4.0, each slot has 2048 chips:
    waveformStruct.NumChips = numPackets * (numSlots * 2048);
    
    % create packets with the DataRate-NumSlots specification:
    singlePacket = struct();
    singlePacket.MACIndex = 0;
    singlePacket.DataRate = dataRate;
    singlePacket.NumSlots = numSlots;
    waveformStruct.PacketSequence = repmat(singlePacket, 1, numPackets);
end

function waveformStruct = createRevisionAConfiguration(packetSize, numSlots, preambleLength, numPackets)

    % top-level waveform configuration:
    waveformStruct = struct();
    waveformStruct.Release = 'RevisionA'; % show Revision on top
    waveformStruct = addCommonWaveformProperties(waveformStruct);
    % according to 12.3.1.3.1 of C.S0024-A v3.0, each slot has 2048 chips.
    % also, according to 10.7.6.1.4, the preamble length (chips) precedes
    % each Physical Layer packet.
    waveformStruct.NumChips = numPackets * (preambleLength + numSlots * 2048);
    
    % create packets with the PacketSize-NumSlots-PreambleLength specification:
    singlePacket = struct();
    singlePacket.MACIndex         = 0;
    singlePacket.PacketSize       = packetSize;
    singlePacket.NumSlots         = numSlots;
    singlePacket.PreambleLength   = preambleLength;
    waveformStruct.PacketSequence = repmat(singlePacket, 1, numPackets);
end

function waveformStruct = addCommonWaveformProperties(waveformStruct)
    
    % properties common between Release 0 and Revision A
    waveformStruct.PNOffset                 = 0;
    waveformStruct.IdleSlotsWithControl     = 'Off';
    waveformStruct.EnableControl            = 'On';
    
    % Filtering and modulation parameters:
    waveformStruct.OversamplingRatio    = 4; 
    waveformStruct.FilterType           = 'cdma2000Long';
    waveformStruct.InvertQ              = 'Off';
    waveformStruct.EnableModulation     = 'Off';
    waveformStruct.ModulationFrequency  = 0;
    
    % create and add packet source:
    packetSource = struct();
    packetSource.MACIndex       = 0;
    packetSource.DataSource     = {'PN9', 1};
    packetSource.EnableCoding   = 'On';    
    waveformStruct.PacketDataSources = packetSource;
end

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