function waveformStruct = cdma2000ReverseReferenceChannels(varargin)
%cdma2000ReverseReferenceChannels Preset reverse cdma2000 waveforms
%   WAVEFORMSTRUCT = cdma2000ReverseReferenceChannels(WAVEFORMID) returns a
%   configuration structure WAVEFORMSTRUCT that can be passed to
%   cdma2000ReverseWaveformGenerator in order to generate the corresponding
%   waveform. WAVEFORMID can take one of the following string values:
%
%   - 'R-PICH-ONLY'  - Reverse Pilot Channel only.
%
%   - Reverse Common Control Channel preset waveforms; 'R-CCCH-DataRate(bps)-FrameLength(ms)' format:
%     'R-CCCH-9600-20',  'R-CCCH-19200-10', 'R-CCCH-19200-20',
%     'R-CCCH-38400-5',  'R-CCCH-38400-10', 'R-CCCH-38400-20' 
%      
%   - Reverse Traffic Channel preset waveforms; 'TRAFFIC-RadioConfiguration-FCHDataRate(bps)' format:
%     'TRAFFIC-RC1-9600',  'TRAFFIC-RC1-4800',  'TRAFFIC-RC1-2400', 'TRAFFIC-RC1-1200', 
%     'TRAFFIC-RC2-14400', 'TRAFFIC-RC2-7200',  'TRAFFIC-RC2-3600', 'TRAFFIC-RC2-1800',
%     'TRAFFIC-RC3-9600',  'TRAFFIC-RC3-4800',  'TRAFFIC-RC3-2700', 'TRAFFIC-RC3-1500',
%     'TRAFFIC-RC4-14400', 'TRAFFIC-RC4-7200',  'TRAFFIC-RC4-3600', 'TRAFFIC-RC4-1800',
%     'TRAFFIC-RC5-9600',  'TRAFFIC-RC5-4800',  'TRAFFIC-RC5-2700', 'TRAFFIC-RC5-1500',
%     'TRAFFIC-RC6-14400', 'TRAFFIC-RC6-7200',  'TRAFFIC-RC6-3600', 'TRAFFIC-RC6-1800'
%
%   - Reverse Enhanced Access Channel preset waveforms; 'R-EACH-DataRate(bps)-FrameLength(ms)' format:
%     'R-EACH-9600-20',  'R-EACH-19200-10',  'R-EACH-19200-20', 
%     'R-EACH-38400-5',  'R-EACH-38400-10',  'R-EACH-38400-20'
%
%   - 'R-PICH-R-FCH' - tests for the mobile station as transmitter - see
%     chapter 4 in C.S0011-E v2.0)
%
%   - Return all channels supported for the specified radio configuration:
%     'ALL-RC1', 'ALL-RC2', 'ALL-RC3', 'ALL-RC4', 'ALL-RC5', 'ALL-RC6'
%
%   WAVEFORMSTRUCT = cdma2000ReverseReferenceChannels(WAVEFORMID, NUMCHIPS)
%   behaves as cdma2000ReverseReferenceChannels(WAVEFORMID), but it also
%   specifies the number of chips. If omitted, NUMCHIPS defaults to 1000.
%
%   WAVEFORMSTRUCT = cdma2000ReverseReferenceChannels('TRAFFIC*', NUMCHIPS, R-SCH-SPEC)
%   also determines the data rate (bps) and frame length (ms) of the Reverse
%   Supplemental Channel (R-SCH) using a 'R-SCH-DataRate-FrameLength' string
%   format. If omitted, R-SCH-SPEC defaults to the ID of the lowest R-SCH data
%   rate allowable for a 20 ms frame length under the specified radio configuration.
%
%   Example 1:
%      config   = cdma2000ReverseReferenceChannels('ALL-RC3');
%      waveform = cdma2000ReverseWaveformGenerator(config)
%
%   Example 2:
%      config   = cdma2000ReverseReferenceChannels('TRAFFIC-RC4-14400', 1000, 'R-SCH-7200-20');
%      waveform = cdma2000ReverseWaveformGenerator(config)
%
%   See also cdma2000ReverseWaveformGenerator, cdma2000ForwardReferenceChannels,
%            evdoReverseReferenceChannels, evdoForwardReferenceChannels

%   Copyright 2015 The MathWorks, Inc.

    % default values:
    if nargin > 0
        [varargin{:}] = convertStringsToChars(varargin{:});
    end
    
    waveformID = 'ALL-RC3';
    numChips = 1000;

    if nargin >= 1
        % 1st argument validation:
        validateattributes(varargin{1}, {'char'}, {'row'}, 'cdma2000ReverseReferenceChannels', 'waveformID');
        waveformID = varargin{1};
        
        if nargin >= 2 % numChips specified. 
            
            % 2nd argument validation:
            validateattributes(varargin{2}, {'numeric'}, {'scalar', 'real', 'integer', 'positive'}, 'cdma2000ReverseReferenceChannels', 'numChips');
            numChips = varargin{2};
            
            if nargin == 3
                
                if ~( any(strncmpi(waveformID, {'TRAFFIC-', 'TRAFFIC_'}, 8)) )
                    id = 'comm:cdma2000evdoParameterGeneration:Rcdma2000ThirdInputNotAllowed';
                    error(id, getString( message(id, waveformID) ) );
                end
                
                % 3rd argument validation:
                validateattributes(varargin{3}, {'char'}, {'row'}, 'cdma2000ReverseReferenceChannels', 'R-SCH-SPEC');
                supSpec = varargin{3};
            elseif nargin >=3
                id = 'comm:cdma2000evdoParameterGeneration:TooManyInputArgs';
                error(id, getString( message(id, 'cdma2000ReverseReferenceChannels', 3) ) );
            end
        end
    end
    
    notR_ALL = false;
    
    % R-PICH-ONLY
    if any(strcmpi(waveformID, {'R-PICH-ONLY', 'RPICH_ONLY', 'RPICH-ONLY', 'R-PICH_ONLY', 'RPICHONLY'}))
        
        % return a waveform containing the pilot channel only
        waveformStruct = createPilotOnly(numChips);
        
    % R-CCCH-*    
    elseif any(strncmpi(waveformID, {'R-CCCH', 'R_CCCH'}, 6)) || strncmpi(waveformID, 'RCCCH', 5)
        
        [dataRate, frameLength] = parseWaveformID(waveformID, notR_ALL); % parse
        
        validateRCCCHorREACHSpecification(dataRate, frameLength, waveformID); % validate
        
        isRCCCH = true;
        waveformStruct = createRCCCHorREACHConfiguration(dataRate, frameLength, numChips, isRCCCH); % create
        
    % TRAFFIC-*
    elseif strncmpi(waveformID, 'TRAFFIC', 7)
        
        [RC, dataRate] = parseWaveformID(waveformID, notR_ALL); % parse
        
        % account for R-SCH specification, either from user or default
        if nargin == 3
            [dataRateSup, frameLengthSup] = parseWaveformID(supSpec, notR_ALL);
        else
            [dataRateSup, frameLengthSup] = getDefaultSupSpec(RC);
        end
        
        % validate:
        validateRadioConfiguration(RC);
        validateRFCHdataRate(RC, dataRate);
        if RC >= 3 && RC <= 6 % R-SCH only defined for RC3-RC6
            validateRSCHConfiguration(RC, dataRateSup, frameLengthSup);
        else % RC == 1 || RC == 2
           if nargin == 3
               % R-SCH specification has been provided, but it is not
               % relevant under this radio configuration
               warningID = 'comm:cdma2000evdoParameterGeneration:NotRelevantRSCHspec';
               warning(warningID, getString( message(warningID, RC) ) );
           end
        end
        
        % create:
        waveformStruct = createR_Traffic_Configuration(RC, dataRate, dataRateSup, frameLengthSup, numChips);
        
    % R-EACH-*    
    elseif any(strncmpi(waveformID, {'R-EACH', 'R_EACH'}, 6)) || strncmpi(waveformID, 'REACH', 5)
        
        [dataRate, frameLength] = parseWaveformID(waveformID, notR_ALL); % parse
        
        validateRCCCHorREACHSpecification(dataRate, frameLength, waveformID); % validate
        
        isRCCCH = false;
        waveformStruct = createRCCCHorREACHConfiguration(dataRate, frameLength, numChips, isRCCCH); % create
        
    % R-PICH-R-FCH
    elseif any(strcmpi(waveformID, {'R-PICH-R-FCH', 'R-PICH_R-FCH', 'R_PICH_R_FCH'}))
    
        % create waveform widely used for testing the mobile station as a
        % transmitter - see Chapter 4 in C.S0011-Ev2.0:
        % http://www.3gpp2.org/Public_html/specs/C.S0011-E_v2.0_20140321.pdf
        waveformStruct = createTXtestWaveform(numChips);
        
    % ALL-*
    elseif strncmpi(waveformID, 'ALL', 3)
        
        RC = parseWaveformID(waveformID, ~notR_ALL);
        
        % validate radio configuration:
        validateRadioConfiguration(RC);
        
        % get all channels supported for that radio configuration:
        waveformStruct = getAllChannels(RC, numChips);
    
    else % Invalid input ID, message points to all acceptable IDs
        invalidIDerror(waveformID);
    end
    
end

function waveformStruct = createPilotOnly(numChips)

    % Any changes in this function will also be reflected in
    % createRCCCHorREACHConfiguration and createTXtestWaveform unless a
    % different R-PICH is used therein

    % top-level waveform configuration
    waveformStruct.RadioConfiguration = 'RC3'; % R-PICH not supported for RC1, RC2
    % add properties common for all Reverse cdma2000 waveforms:
    waveformStruct = addCommonWaveformProperties(waveformStruct);
    waveformStruct.NumChips = numChips;
    
    % create default Reverse Pilot Channel (R-PICH):
    waveformStruct.RPICH    = getDefaultRPICH();
end

function channelStruct = getDefaultRPICH()
    channelStruct.Enable                = 'On';
    channelStruct.Power                 = 0;
    channelStruct.LongCodeMask          = 0;
    channelStruct.PowerControlEnable    = 'Off';
end

function waveformStruct = addCommonWaveformProperties(waveformStruct)
    
    % common properties for all Reverse cdma2000 waveforms
    
    waveformStruct.PowerNormalization = 'Off';
    
    % Filtering and modulation parameters:
    waveformStruct.OversamplingRatio    = 4; 
    waveformStruct.FilterType           = 'cdma2000Long';
    waveformStruct.InvertQ              = 'Off';
    waveformStruct.EnableModulation     = 'Off';
    waveformStruct.ModulationFrequency  = 0;
end

function channelStruct = getCommonChannelProperties()

    % these properties are used identically by all Reverse cdma2000
    % channels except for R-PICH:
    channelStruct.Enable                = 'On';
    channelStruct.Power                 = 0;
    channelStruct.LongCodeMask          = 0;
    channelStruct.EnableCoding          = 'On';
    channelStruct.DataSource            = {'PN9', 1};
end

function [number1, number2] = parseWaveformID(waveformID, isR_ALL)
        
    % parses only 1 number from the ID if isR_ALL is true, otherwise it parses 2

    delimiter = '-';
    
    % get first substring until delimiter ('-')
    [~, remain] = strtok(waveformID, delimiter);
    
    % discard the first token which does not contain data, e.g., R-, TRAFFIC, ALL
    
    % get second substring until next delimiter ('-')
    [token, remain] = strtok(remain, delimiter);
    if any(strcmpi(token, {'CCCH', 'EACH', 'SCH'} ))
        [token, remain] = strtok(remain, delimiter); % discard CCCH/EACH/SCH
    end
    
    if strncmpi(token, 'RC', 2)
        token(1:2) = []; % remove 'RC' from e.g., RC3, and only keep the number
    end
    
    % 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 (isR_ALL && ~isempty(remain)) || (~isR_ALL && isempty(remain))
        invalidIDerror(waveformID);
    end
    
    % parse a 2nd optional number (R-CCCH, TRAFFIC, R-EACH, R-SCH cases)
    if ~isR_ALL % 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 validateRCCCHorREACHSpecification(dataRate, frameLength, waveformID)

    % The Reverse Common Control Channel (R-CCCH) and the Reverse Enhanced
    % Access Channel (R-EACH) have symmetric presets, thus a common
    % function is used

    % combinations allowed by the standard, see Tables 2.1.3.1.3 - 1&2 in C.S0002-Ev3.0:
    % http://www.3gpp2.org/Public_html/specs/C.S0002-F v2.0_20140519.pdf
    %                     DataRate      FrameLength
    validCombinations = [   9600            20; ...
                            19200           10; ...
                            19200           20; ...
                            38400           5;  ...
                            38400           10;  ...
                            38400           20 ];
                        
    if ~any( ismember(validCombinations, [dataRate frameLength], 'rows') )
        invalidIDerror(waveformID);
    end
end

function waveformStruct = createRCCCHorREACHConfiguration(dataRate, frameLength, numChips, RCCCHnotREACH)

    % The Reverse Common Control Channel (R-CCCH) and the Reverse Enhanced
    % Access Channel (R-EACH) have symmetric presets, thus a common
    % function is used

    % start with an R-PICH-ONLY waveform
    waveformStruct = createPilotOnly(numChips);

    % then add the R-CCCH or R-EACH:
    channel             = getCommonChannelProperties();
    channel.DataRate    = dataRate;
    channel.FrameLength = frameLength;
    channel.WalshCode   = 1;
    if RCCCHnotREACH
        waveformStruct.RCCCH = channel;
    else
        waveformStruct.REACH = channel;
    end
end

function [dataRateSup, frameLengthSup] = getDefaultSupSpec(RC)

    % if no R-SCH ID is specified, then the default configuration
    % corresponds to the lowest allowable data rate for 20 ms frame length
    % in the Reverse Supplemental Channel (R-SCH)
    frameLengthSup = 20;
    
    switch RC
        case 1
            % in this case R-SCCH is used, not R-SCH
            dataRateSup = 9600;
        case 2
            % in this case R-SCCH is used, not R-SCH
            dataRateSup = 14400;
        case {3, 5}
            dataRateSup = 1500;
        case {4, 6}
            dataRateSup = 1800;
        otherwise
            dataRateSup = []; % validateRadioConfiguration will error out
    end
end

function validateRadioConfiguration(RC)
    % the standard specifies 8 RCs, but the waveform generation functions
    % only support RC 1-6
    supportedRCs = 1:6;
    
    if ~any(ismember(supportedRCs, RC))
        id = 'comm:cdma2000evdoParameterGeneration:RUnsupportedRC';
        error(id, getString(message(id)));
    end
end

function validateRFCHdataRate(RC, dataRate)

    % values allowed by the standard for the Reverse Fundamental Channel
    % (R-FCH), see Tables 2.1.3.1.3 - 1&2 in C.S0002-Ev2.0
    if RC == 1
        validFCHRates = [1200 2400 4800 9600];
    elseif RC == 2 || RC == 4 || RC == 6
        validFCHRates = [1800 3600 7200 14400];
        % rate 9600, framelength = 5 is dropped to simplify the interface
        % it can still be specified via post-processing
    elseif RC == 3 || RC == 5
        validFCHRates = [1500 2700 4800 9600];
    end
   
    if ~any(ismember(validFCHRates, dataRate))
        waveformID = ['TRAFFIC-RC' num2str(RC) '-' num2str(dataRate)];
        invalidIDerror(waveformID);
    end
end

function validateRSCHConfiguration(RC, dataRateSup, frameLengthSup)
    
    % combinations allowed by the standard for the Reverse Supplemental
    % Channel (R-SCH), see Tables 2.1.3.1.3 - 1&2 in C.S0002-Ev3.0:
    % http://www.3gpp2.org/Public_html/specs/C.S0002-F v2.0_20140519.pdf
    
    if RC == 3 || RC == 5
    %                         DataRate      FrameLength
        validCombinations = [   1500            20; 
                                2700            20; ...
                                4800            20; ...
                                9600            20; ...
                                19200           20; ...
                                38400           20; ... 
                                76800           20; ... 
                                153600          20; ... 
                                307200          20; ... 
                                1350            40; ...
                                2400            40; ...
                                4800            40; ...
                                9600            40; ...
                                19200           40; ...
                                38400           40; ... 
                                76800           40; ... 
                                153600          40; ... 
                                1200            80; ...
                                2400            80; ...
                                4800            80; ...
                                9600            80; ...
                                19200           80; ...
                                38400           80; ... 
                                76800           80  ];
        if RC == 5 
            % some additional combinations also apply
            validCombinations = [validCombinations;
                                614400          20; ...
                                307200          40; ...
                                153600          80];
        end
        
    elseif RC == 4 || RC == 6
        %                     DataRate      FrameLength
        validCombinations = [   1800            20; ...
                                3600            20; ...
                                7200            20; ...
                                14400           20; ...
                                28800           20; ...
                                57600           20; ... 
                                115200          20; ... 
                                230400          20; ... 
                                1800            40; ...
                                3600            40; ...
                                7200            40; ...
                                14400           40; ...
                                28800           40; ...
                                57600           40; ... 
                                115200          40; ... 
                                1800            80; ...
                                3600            80; ...
                                7200            80; ...
                                14400           80; ...
                                28800           80; ...
                                57600           80  ];
        if RC == 6
            % some additional combinations also apply
            validCombinations = [validCombinations;
                                460800          20; ...
                                1036800         20; ...
                                230400          40; ...
                                518400          40; ...
                                115200          80; ...
                                259200          80];
        end
    end
                        
    waveformID = ['R-SCH-' num2str(dataRateSup) '-' num2str(frameLengthSup)];
    if ~any( ismember(validCombinations, [dataRateSup frameLengthSup], 'rows') )
        invalidRSCHforRCerror(waveformID, RC);
    end
end

function waveformStruct = createR_Traffic_Configuration(RC, dataRate, dataRateSup, frameLengthSup, numChips)

    % top-level waveform configuration:
    waveformStruct.RadioConfiguration = ['RC' num2str(RC)];
    waveformStruct = addCommonWaveformProperties(waveformStruct);
    waveformStruct.NumChips = numChips;
    
    % add a Reverse Fundamental Channel (R-FCH):
    rfch                = getCommonChannelProperties();
    rfch.DataRate       = dataRate;
    rfch.FrameLength    = 20;
    rfch.WalshCode      = 3;
    waveformStruct.RFCH = rfch;
    
    if RC == 1 || RC == 2
        
        % also add a Reverse Supplemental Code Channel (R-SCCH)
        rscch               = getCommonChannelProperties();
        % the standard specifies a single R-SCCH rate for each of RC1, RC2
        if RC == 1
            rscch.DataRate  = 9600;
        else % RC == 2
            rscch.DataRate  = 14400;
        end
        waveformStruct.RSCCH = rscch;
        
    elseif RC >= 3 && RC <= 6
        
        % else add:
        % - two Reverse Supplemental Channels (R-SCH1 and R-SCH2) 
        % - and a Reverse Pilot Channel (R-PICH)
        
        % Reverse Supplemental Channels (R-SCH1) & (R-SCH2):
        rsch                = getCommonChannelProperties();
        rsch.DataRate       = dataRateSup;
        rsch.FrameLength    = frameLengthSup;
        if any(dataRateSup == [307200 153600 230400 57600 614400 460800 1036800])
            rsch.WalshLength    = 2;
        else
            rsch.WalshLength    = 4;
        end
        rsch.WalshCode      = 0;
        waveformStruct.RSCH1 = rsch;
        % Make RSCH2 identical to RSCH1, with the only exception being the WalshCode
        waveformStruct.RSCH2 = rsch;
        waveformStruct.RSCH2.WalshCode = 1;
        
        % Reverse Pilot Channel (R-PICH)
        waveformStruct.RPICH    = getDefaultRPICH();
    end
end

function waveformStruct = createTXtestWaveform(numChips)

    % create waveform widely used for testing the mobile station as a
    % transmitter - see Chapter 4 in C.S0011-Ev2.0

    % start with a Reverse Pilot Channel (R-PICH-ONLY) waveform:
    waveformStruct = createPilotOnly(numChips); 
    waveformStruct.RPICH.Power = -7;
    
    % add a traffic channel: Reverse Fundamental Traffic Channel (R-FCH)
    rfch                = getCommonChannelProperties();    
    rfch.Power          = -7.4;
    rfch.DataRate       = 9600;
    rfch.FrameLength    = 20;
    rfch.WalshCode      = 1;
    waveformStruct.RFCH = rfch;
end

function waveformStruct = getAllChannels(RC, numChips)

    % return all channels allowed under the specified radio configuration

    waveformStruct.RadioConfiguration = ['RC' num2str(RC)]; % add RC to the top
    waveformStruct = addCommonWaveformProperties(waveformStruct); % add common defaults
    waveformStruct.NumChips = numChips;
    
    % first add the channels present in all Radio Configurations:    
    
    % default rate selection used by multiple channels
    if RC == 1 || RC == 3 || RC == 5
       defaultRate = 9600;
    else % RC == 2 || RC == 4 || RC == 6
       defaultRate = 14400;
    end
    
    % Reverse Fundamental Channel (R-FCH)
    rfch               = getCommonChannelProperties();
    rfch.DataRate      = defaultRate;
    rfch.FrameLength   = 20;
    rfch.WalshCode     = 1;
    waveformStruct.RFCH = rfch;
    
    
    % next add Radio-Configuration-specific channels:
    
    if RC == 1 || RC == 2
        
        % Reverse Access Channel (R-ACH)
        rach                = getCommonChannelProperties();
        waveformStruct.RACH = rach;
        
        % Reverse Supplemental Code Channel (R-SCCH)
        rscch                = getCommonChannelProperties();
        rscch.DataRate       = defaultRate;
        waveformStruct.RSCCH = rscch;
        
    elseif RC >= 3 && RC <= 6
        
        % Reverse Pilot Channel (R-PICH)
        waveformStruct.RPICH = getDefaultRPICH();
        
        % Reverse Enhanced Access Channel (R-EACH)
        reach               = getCommonChannelProperties();
        reach.DataRate      = 9600;
        reach.FrameLength   = 20;
        reach.WalshCode     = 2;
        waveformStruct.REACH = reach;
        
        % Reverse Common Control Channel (R-CCCH)
        rccch               = getCommonChannelProperties();
        rccch.DataRate      = 9600;
        rccch.FrameLength   = 20;
        rccch.WalshCode     = 3;
        waveformStruct.RCCCH = rccch;
        
        % Reverse Dedicated Control Channel (R-DCCH)
        rdcch               = getCommonChannelProperties();
        rdcch.DataRate      = defaultRate;
        rdcch.FrameLength   = 20;
        rdcch.WalshCode     = 4;
        waveformStruct.RDCCH = rdcch;
        
        % Reverse Supplemental Channels (R-SCH1 & R-SCH2)
        rsch                = getCommonChannelProperties();
        rsch.DataRate       = defaultRate;
        rsch.FrameLength    = 20;
        rsch.WalshLength    = 8;
        rsch.WalshCode      = 5;
        waveformStruct.RSCH1 = rsch;
        % make R-SCH1 and R-SCH2 identical, with the only exception being the WalshCode:
        waveformStruct.RSCH2 = rsch;
        waveformStruct.RSCH2.WalshCode = 6;
    end
end

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

function invalidRSCHforRCerror(rschID, RC)
    invalidID       = 'comm:cdma2000evdoParameterGeneration:InvalidRSCH';
    thisFunction    = 'cdma2000ReverseReferenceChannels';
    error(invalidID, getString( message(invalidID, rschID, RC, thisFunction)));
end
