function info = hPRACHOFDMInfo(carrier,prach)
%hPRACHOFDMInfo PRACH OFDM modulation related information
%   INFO = hPRACHOFDMInfo(CARRIER,PRACH) provides dimensional information
%   related to physical random access channel (PRACH) OFDM modulation,
%   given uplink carrier configuration object CARRIER and PRACH
%   configuration object PRACH.
%
%   CARRIER is a carrier-specific configuration object, as described in
%   <a href="matlab:help('nrCarrierConfig')"
%   >nrCarrierConfig</a> with these properties:
%
%   SubcarrierSpacing   - Subcarrier spacing in kHz (15, 30, 60, 120, 240)
%   CyclicPrefix        - Cyclic prefix ('normal', 'extended')
%   NSizeGrid           - Number of resource blocks in carrier resource
%                         grid (1...275)
%
%   PRACH is a PRACH-specific configuration object, as described in
%   <a href="matlab:help('nrPRACHConfig')"
%   >nrPRACHConfig</a> with these properties:
%
%   FrequencyRange      - Frequency range (used in combination with
%                         DuplexMode to select a configuration table from
%                         TS 38.211 Table 6.3.3.2-2 to 6.3.3.2-4)
%   DuplexMode          - Duplex mode (used in combination with
%                         FrequencyRange to select a configuration table
%                         from TS 38.211 Table 6.3.3.2-2 to 6.3.3.2-4)
%   ConfigurationIndex  - Configuration index, as defined in TS 38.211
%                         Tables 6.3.3.2-2 to 6.3.3.2-4
%   SubcarrierSpacing   - PRACH subcarrier spacing in kHz
%   NPRACHSlot          - PRACH slot number
%
%   INFO is a structure containing the fields:
%
%   SamplingRate        - The sampling rate of the OFDM modulator
%   Nfft                - The number of FFT points used in the OFDM 
%                         modulator
%   Windowing           - The number of time-domain samples over which 
%                         windowing and overlapping of OFDM symbols is 
%                         applied
%   CyclicPrefixLengths - Cyclic prefix length (in samples) of each OFDM 
%                         symbol
%   GuardLengths        - Guard length (in samples) of each OFDM symbol
%   SymbolLengths       - Symbol length (in samples) of each OFDM symbol
%   OffsetLength        - Length (in samples) of the initial time offset
%                         from the start of the configured PRACH slot 
%                         period to the start of the cyclic prefix
%   NSubcarriers        - Number of subcarriers
%   SubcarrierSpacing   - Subcarrier spacing in kHz
%   TotSubframes        - Total number of subframes spanned by the nominal 
%                         PRACH slot
%   PRACHSlotsPerPeriod - Number of PRACH slots in the overall period that
%                         spans an integer multiple of 'x' frames, where
%                         'x' is given by TS 38.211 Tables 6.3.3.2-2
%                         to 6.3.3.2-4
%
%   Note that for long formats (LRA=839), the first subframe of a PRACH
%   preamble can occur partway through the nominal PRACH slot period. In
%   this case, INFO.OffsetLength is increased to ensure that the OFDM
%   waveform produced by hPRACHOFDMModulate will span the entire active
%   PRACH preamble. To balance these longer PRACH slots with the nominal
%   PRACH slot period, some inactive PRACH slots will have OFDM waveforms
%   that are shorter than the nominal PRACH slot period. This is conveyed
%   by INFO.CyclicPrefixLengths and INFO.GuardLengths being empty (i.e. no
%   OFDM symbols) and INFO.OffsetLength will equal the number of empty
%   subframes required. Note that INFO.TotSubframes is equal to the nominal
%   PRACH slot duration in all cases, whereas the actual length in samples
%   of a particular PRACH slot is given by:
%   INFO.OffsetLength + sum(INFO.SymbolLengths)
%
%   Example 1: 
%   % Generate PRACH OFDM information for preamble format A1.
%
%   % Configure carrier
%   carrier = nrCarrierConfig;
%
%   % Configure PRACH for format A1
%   prach = nrPRACHConfig;
%   prach.ConfigurationIndex = 106;
%   prach.SubcarrierSpacing = 15;
%
%   % Create PRACH OFDM information
%   info = hPRACHOFDMInfo(carrier,prach)
%
%   Example 2:
%   % Generate PRACH OFDM information for multiple PRACH slots and show
%   % how the actual PRACH slot duration deviates from the nominal duration
%   % to ensure that the slot will always span entire active PRACH 
%   % preambles.
%
%   % Create carrier and PRACH configurations, calculate OFDM information
%   % and observe the nominal PRACH slot duration given by totSubframes:
%
%   carrier = nrCarrierConfig;
%   prach = nrPRACHConfig;
%   prach.DuplexMode = 'FDD';
%   prach.ConfigurationIndex = 47;
%   ofdmInfo = hPRACHOFDMInfo(carrier,prach);
%   totSubframes = ofdmInfo.TotSubframes
% 
%   % Use configuration tables to establish starting subframes for active
%   % PRACH preambles:
%
%   configTable = prach.Tables.ConfigurationsFR1PairedSUL;
%   subframeNumber = configTable.SubframeNumber{prach.ConfigurationIndex+1,:}
%
%   % The first nominal PRACH slot period, lasting totSubframes = 3 
%   % subframes, will span subframes 0, 1 and 2. However, the first active
%   % PRACH preamble should start in subframe 1 and end in subframe 3. 
%   % Therefore the PRACH slot must contain an additional subframe to 
%   % ensure that the PRACH slot spans the whole active preamble. The
%   % output produced by hPRACHOFDMInfo reflects this information, as the
%   % OffsetLength field is set to 1 subframe:
%
%   ofdmInfo.OffsetLength
%
%   % For NPRACHSlot = 1, the output of hPRACHOFDMInfo shows that the 
%   % lengths of the symbol, cyclic prefix, and guard period are empty and
%   % the offset length is zero. Therefore the corresponding waveform will 
%   % last zero subframes. This is to balance longer PRACH slots (such as
%   % for NPRACHSlot = 0 above) with the nominal PRACH slot period:
%
%   prach.NPRACHSlot = 1;
%   ofdmInfo = hPRACHOFDMInfo(carrier,prach)
% 
%   % Note that the next slot (NPRACHSlot = 2) will have a value of 
%   % OffsetLength equal to two subframes. The first subframe of the PRACH 
%   % slot is subframe 4 and without the additional subframes, it would 
%   % span subframes 4, 5 and 6. However, a PRACH preamble should start in 
%   % subframe 6 and occupy subframes 6, 7 and 8, so two extra subframe are
%   % required:
%
%   prach.NPRACHSlot = 2;
%   ofdmInfo = hPRACHOFDMInfo(carrier,prach)
%
%   See also hPRACHOFDMModulate, nrPRACHGrid, nrPRACH, nrPRACHIndices.

%   Copyright 2019 The MathWorks, Inc.

    internalinfo = OFDMInfo(carrier,prach);
    
    info.SamplingRate = internalinfo.SamplingRate;
    info.Nfft = internalinfo.Nfft;
    info.Windowing = 0;
    info.CyclicPrefixLengths = internalinfo.CyclicPrefixLengths;
    info.GuardLengths = internalinfo.GuardLengths;
    info.SymbolLengths = internalinfo.CyclicPrefixLengths + internalinfo.Nfft + internalinfo.GuardLengths;
    info.OffsetLength = internalinfo.OffsetLength;
    info.NSubcarriers = internalinfo.K;
    info.SubcarrierSpacing = prach.SubcarrierSpacing;
    info.TotSubframes = internalinfo.TotSubframes;
    info.PRACHSlotsPerPeriod = internalinfo.PRACHSlotsPerPeriod;
    
end

function info = OFDMInfo(carrier,prach)
    
    % Sampling rate corresponding to 1/T_s where T_s is defined in TS
    % 38.211 Section 4.1. This will be adopted as the nominal sampling rate
    % for calculations here
    sr_nominal = 30.72e6;
    
    % Nominal IDFT size (i.e. in terms of the sampling rate above). This
    % corresponds to parts of the expressions for N_u in TS 38.211 Tables
    % 6.3.3.1-1 and 6.3.3.1-2:
    % For formats 0, 1 and 2: 24576
    % For format 3: 6144
    % For short formats: 2048 * 2^-mu
    n_idft_nominal = 2048 * 15 / prach.SubcarrierSpacing;
    
    % Get PRACH slot grid size and record the number of subcarriers K and
    % the number of OFDM symbols L
    siz = size(nrPRACHGrid(carrier,prach));
    K = siz(1);
    L = siz(2);
    
    % Calculate OFDM information for TS 38.211 Section 5.3.1 "OFDM
    % basedband signal generation for all channels except PRACH", for the
    % numerology described in TS 38.211 Section 5.3.2
    LRA = prach.LRA;
    if (LRA==839)
        % For long sequences (LRA=839) numerology mu=0 is assumed
        deltaf_RA = 15;
    else % LRA==139
        % For short sequences (LRA=139) the numerology given by the PRACH
        % subcarrier spacing is used
        deltaf_RA = prach.SubcarrierSpacing;
    end
    uemu.NRB = K/12;
    uemu.SubcarrierSpacing = deltaf_RA;
    uemu.CyclicPrefix = 'Normal';
    muinfo = hOFDMInfo(uemu);
    
    % Calculate OFDM symbol start samples n_mu_start_l. These are the
    % sample indices corresponding to t_mu_start_l described in TS 38.211
    % Section 5.3.2, calculated at the nominal sampling rate. N_mu_u and
    % N_mu_CP are also defined in Section 5.3.2. n_mu_tot_l is the number
    % of samples to the end of OFDM symbol 'l' and is used subsequently for
    % calculating guard lengths
    N_mu_u = muinfo.Nfft * sr_nominal / muinfo.SamplingRate;
    N_mu_CP = muinfo.CyclicPrefixLengths * sr_nominal / muinfo.SamplingRate;
    nSlot = mod(prach.NPRACHSlot,muinfo.SlotsPerSubframe);
    start_instant = sum(N_mu_CP(1:nSlot*muinfo.SymbolsPerSlot) + N_mu_u);
    N_mu_CP = N_mu_CP(nSlot*muinfo.SymbolsPerSlot + (1:muinfo.SymbolsPerSlot));
    n_mu_tot_l = cumsum(N_mu_CP + N_mu_u);
    n_mu_start_l = [0 n_mu_tot_l(1:end-1)];

    % In the case of format C0, each preamble has one active sequence
    % period (see Table 6.3.3.1-2) but including the guard and the cyclic
    % prefix, the preamble spans two OFDM symbols. For this reason, the
    % slot grid related to format C0 has 7 OFDM symbols, rather than 14.
    % Therefore, the symbol indices 'l_starts' and 'l_ends' that address
    % the PRACH slot grid must be adjusted by the function 'l_mu_fn' here
    % to correctly address the OFDM information for numerology mu (for
    % other formats, no adjustment is required)
    prachFormat = prach.Format;
    if (strcmpi(prachFormat,'C0'))
        l_mu_fn = @(x)(x*2 + 1);
    else
        l_mu_fn = @(x)x;
    end
    
    % Get starting OFDM symbol l_0 of the first PRACH time occasion,
    % defined in TS 38.211 Section 5.3.2
    switch upper(prach.FrequencyRange)
        case 'FR1'
            if strcmpi(prach.DuplexMode,'TDD') % TS 38.211 Table 6.3.3.2-3
                configTable = prach.Tables.ConfigurationsFR1Unpaired;
            else % TS 38.211 Table 6.3.3.2-2
                configTable = prach.Tables.ConfigurationsFR1PairedSUL;
            end
        otherwise % TS 38.211 Table 6.3.3.2-4
            configTable = prach.Tables.ConfigurationsFR2;
    end
    l_0 = configTable.StartingSymbol(prach.ConfigurationIndex+1);
    
    % For long sequences (LRA=839) and non-zero l_0, the starting position
    % is implemented as an initial guard length N_offset
    straddleSF = false;
    if (LRA==839 && l_0 > 0)
        N_offset = n_mu_tot_l(l_0);
        l_0 = 0;
        % For preamble formats 0 and 3, record that an active PRACH
        % preamble straddles an extra subframe due to the non-zero l_0
        if (any(strcmpi(prachFormat,{'0','3'})))
            straddleSF = true;
        end
    else
        N_offset = 0;
    end
    
    % Get the total number of PRACH time occasions n_RA_t, defined in TS
    % 38.211 Section 5.3.2
    n_RA_t = prach.NumTimeOccasions;
    
    % Get the duration N_RA_dur in OFDM symbols of one PRACH time occasion,
    % defined in TS 38.211 Section 5.3.2
    N_RA_dur = prach.PRACHDuration;
    
    % In the case of format C0, each preamble has one active sequence
    % period (see Table 6.3.3.1-2) but including the guard and the cyclic
    % prefix, the preamble spans two OFDM symbols. For this reason, the
    % slot grid related to format C0 has 7 OFDM symbols, rather than 14,
    % and each value related to it that is derived directly from TS 38.211
    % is halved
    if (strcmpi(prachFormat,'C0'))
        l_0 = l_0 / 2;
    end
    
    % Get the vector of OFDM symbol indices l_starts (0-based) which
    % correspond to the starting symbol of each PRACH time occasion in the
    % PRACH slot. TS 38.211 Section 5.3.2 only considers a single 'l' for
    % the start of a given PRACH time occasion, this vector contains all
    % values of 'l'
    l_starts = l_0 + ((0:(n_RA_t-1)) * N_RA_dur);
    
    % If the starting OFDM symbol l_0 of the first PRACH time occasion is
    % non-zero, add OFDM symbol zero to the vector of starting symbol
    % indices so that total duration of OFDM symbols 0...(l_0-1) is
    % correctly accounted for in subsequent calculations
    if (l_0 > 0)
        l_starts = [0 l_starts];
    end
    
    % Get the cyclic prefix length N_RA_CP and useful OFDM symbol period
    % N_u from TS 38.211 Table 6.3.3.1-1 or 6.3.3.1-2
    if (LRA==839)
        formatTable = prach.Tables.LongPreambleFormats;
    else % LRA==139
        formatTable = prach.Tables.ShortPreambleFormats;
    end
    N_RA_CP = formatTable.N_CP(strcmpi(formatTable.Format,prachFormat));
    N_u = formatTable.N_u(strcmpi(formatTable.Format,prachFormat));
    
    % For short sequences (LRA=139), adjust cyclic prefix lengths and
    % useful lengths to account for numerology (2^-mu term in TS 38.211
    % Table 6.3.3.1-2)
    if (LRA==139)
        N_RA_CP = N_RA_CP * n_idft_nominal / 2048;
        N_u = N_u * n_idft_nominal / 2048;
    end
    
    % Calculate cyclic prefix lengths N_RA_CP_l as described in TS 38.211
    % Section 5.3.2, at nominal sampling rate. Note that the vector
    % N_RA_CP_l only has non-zero values in the positions where 'l'
    % corresponds to the first OFDM symbol of a PRACH time occasion. Note
    % that format C2 has a cyclic prefix length equal to the nominal IDFT
    % size, which is implemented in nrPRACH and nrPRACHIndices as an extra
    % initial repetition of nominal sequence, so the cyclic prefix length
    % is zero here. The variable 'start_instant' takes into account the
    % position of the PRACH slot within the carrier subframe
    N_RA_CP_l = zeros([1 L]);
    if (~strcmpi(prachFormat,'C2'))
        N_RA_CP_l(l_starts + 1) = N_RA_CP;
        if (l_0 > 0)
            N_RA_CP_l(1) = 0;
        end
    end
    
    % For short sequences (LRA=139), the cyclic prefix for each PRACH
    % occasion will be extended by n=16 samples either zero, one, or two
    % times depending on how many times the PRACH occasion crosses time
    % instants 0 and 0.5 ms (note that for format C2, this extends the CPs
    % from zero to 'n' samples)
    if (LRA==139)
        n0dot5ms = sr_nominal * 0.5 * 1e-3;
        interval_end = n_mu_start_l(l_mu_fn(l_starts) + 1).' + N_u + N_RA_CP;
        if (l_0 > 0)
            % The interval corresponding to non-zero l_0 spans OFDM symbols
            % 0...(l_0-1), so the interval ends at the start of the second 
            % interval (OFDM symbol l_0), given by l_starts(2)
            interval_end(1) = n_mu_start_l(l_mu_fn(l_starts(2)));
        end
        n = 16;
        for instant = mod([0 n0dot5ms] - start_instant,n0dot5ms*2)
            i = find(interval_end > instant,1,'first');
            if (~isempty(i))
                l = l_starts(i) + 1;
                N_RA_CP_l(l) = N_RA_CP_l(l) + n;
            end
        end
    end
    
    % Calculate guard lengths N_GP_l at nominal sampling rate. The guard
    % lengths are not explicitly mentioned in TS 38.211, but they span the
    % interval between (n_RA_start + N_u + N_RA_CP_l) for one PRACH time
    % occasion and n_RA_start for the next PRACH time occasion (or the end
    % of the PRACH slot). n_RA_start is the number of samples corresponding
    % to t_RA_start, calculated at the nominal sampling rate. Note that the
    % useful OFDM symbol period N_u for a PRACH time occasion is split
    % between multiple OFDM symbols, each having useful period
    % n_idft_nominal, with only the first OFDM symbol having a cyclic
    % prefix and only the last having a guard. Therefore N_GP_l only has
    % non-zero values in the positions where 'l' corresponds to the last
    % OFDM symbol of a PRACH time occasion (or in the last OFDM symbol of
    % the PRACH slot). The final guard length of the PRACH slot pads the
    % PRACH slot length up to an integer multiple of the slot length of the
    % reference numerology mu
    N_GP_l = zeros([1 L]);
    l_ends = l_starts + N_RA_dur - 1;
    if (l_0 > 0)
        l_ends = [(l_0 - 1) l_ends(2:end)];
    end
    l_ends(l_ends==(L-1)) = [];
    n_RA_tot_l = cumsum(n_idft_nominal + N_RA_CP_l);
    N_GP_l(l_ends + 1) = diff([0 n_mu_tot_l(l_mu_fn(l_ends)+1) - n_RA_tot_l(l_ends + 1)]);
    negIdx = find(N_GP_l(l_ends + 1) < 0);
    N_GP_l(l_ends(negIdx-1) + 1) = N_GP_l(l_ends(negIdx-1) + 1) + N_GP_l(l_ends(negIdx) + 1);
    N_GP_l(l_ends(negIdx) + 1) = 0;
    n_RA_end = N_offset + n_RA_tot_l(end) + sum(N_GP_l);
    n_mu_end = n_mu_tot_l(end);
    N_GP_l(end) = (n_mu_end * ceil(n_RA_end / n_mu_end)) - n_RA_end;        
    
    % Calculate the number of samples per subframe
    samplesPerSubframe = (sr_nominal * 1e-3);
    
    % Calculate total number of subframes spanned by a nominal PRACH slot
    if (LRA==139)
        totSubframes = 1 / muinfo.SlotsPerSubframe;
    else
        totSubframes = (N_offset + sum(N_RA_CP_l + n_idft_nominal + N_GP_l)) / samplesPerSubframe;
        totSubframes = totSubframes - straddleSF;
    end

    % For long sequences (LRA=839), establish the adjustment to the number
    % of subframes 'aSF' required to account for cases where the starting
    % subframe of the PRACH preamble occurs partway through the nominal
    % PRACH slot period
    if (LRA==839)
        aSF = getSubframeAdjustment(prach,configTable,totSubframes,straddleSF);
        if (aSF >= 0)
            % Add the appropriate number of extra samples to 'N_offset'
            N_offset = N_offset + (aSF * samplesPerSubframe);
        else
            % If 'aSF' is negative, this signals that a waveform shorter
            % than the nominal PRACH slot period should be produced for the
            % current (inactive) PRACH slot, to balance PRACH slots with
            % extra subframes against the nominal PRACH slot period. The
            % OFDM information here results in an empty waveform of
            % duration 'N_offset' being produced by hPRACHOFDMModulate
            N_offset = (totSubframes + straddleSF + aSF) * samplesPerSubframe;
            N_RA_CP_l = [];
            N_GP_l = [];
        end
    end
    
    % Calculate number of PRACH slots in the overall period that spans an
    % integer multiple of 'x' frames
    x = configTable.x(prach.ConfigurationIndex+1);
    nPRACHSlots = lcm(x*10,max([1 totSubframes])) / totSubframes;
    
    % Calculate carrier OFDM information
    ue.NRB = carrier.NSizeGrid;
    ue.SubcarrierSpacing = carrier.SubcarrierSpacing;
    ue.CyclicPrefix = carrier.CyclicPrefix;
    carrierinfo = hOFDMInfo(ue);
    
    % Determine ratio 'R' between carrier sampling rate and nominal
    % sampling rate
    R = carrierinfo.SamplingRate / sr_nominal;
    
    % Create OFDM information output structure w.r.t. carrier sampling rate
    info.K = K;
    info.Nfft = n_idft_nominal * R;
    info.SamplingRate = carrierinfo.SamplingRate;
    info.CyclicPrefixLengths = N_RA_CP_l * R;
    info.GuardLengths = N_GP_l * R;
    info.OffsetLength = N_offset * R;
    info.TotSubframes = totSubframes;
    info.PRACHSlotsPerPeriod = nPRACHSlots;
    
end

% For long sequences (LRA=839), establish the adjustment to the number of
% subframes 'aSF' required to account for cases where the starting subframe
% of the PRACH preamble occurs partway through the nominal PRACH slot
% period. The nominal periods are defined here as intervals of
% 'totSubframes' duration, starting at subframe 0 of frame n_SFN mod x = 0
% where 'x' is given by the PRACH configuration table. Note that 'aSF' can
% be negative, indicating by how many subframes an inactive PRACH slot
% should be shortened. Example 2 for hPRACHOFDMInfo above shows the OFDM
% information for NPRACHSlot = 0, 1 and 2. The corresponding values of 
% 'aSF' produced by getSubframeAdjustment are 1, -3 and 2. 
function aSF = getSubframeAdjustment(prach,configTable,totSubframes,straddleSF)

    % Determine the set of subframe numbers where PRACH preambles start
    % within the period of 'x' frames
    y = configTable.y{prach.ConfigurationIndex+1};
    x = configTable.x(prach.ConfigurationIndex+1);
    startSFs = configTable.SubframeNumber{(prach.ConfigurationIndex+1)};
    startSFs = startSFs + (y * 10);
    startSFs = startSFs(:) + (x * 10 * (0:(totSubframes-1)));
    startSFs = startSFs(:).';
    
    % Determine the overall periodicity and starting subframes of the
    % nominal PRACH slots
    nPRACHSlots = lcm(x*10,totSubframes) / totSubframes;
    nominalSFs = [0 cumsum(repmat(totSubframes,1,nPRACHSlots-1))];
    
    % Establish which nominal PRACH slots are active, and their 
    % corresponding PRACH slot indices
    activeSlotFn = @(n)any(startSFs>=n & startSFs<=(n+totSubframes-1));
    activePRACHSlot = arrayfun(activeSlotFn,nominalSFs);
    activeIndex = find(activePRACHSlot);
    
    % Adjust the number of subframes in the PRACH slots:
    % (a) where the starting subframe of the PRACH preamble occurs 
    %     partway through the nominal PRACH slot period, and therefore the 
    %     PRACH slot needs lengthened to fully span the preamble
    % (b) where the lengthening of PRACH slots according to (a) above
    %     results in the PRACH slot timeline running behind the nominal
    %     timeline, such that a PRACH preamble cannot start "on time". In 
    %     this case, an earlier inactive PRACH slot needs to be shortened
    slotSFs = nominalSFs;
    aSF = zeros(size(slotSFs));
    % For each active PRACH slot index
    for i = 1:numel(activeIndex)
        % (a) Calculate extra slots required
        idxa = activeIndex(i);
        delta = startSFs(i) - slotSFs(idxa);
        if (delta < 0)
            % (b) If the number of extra slots is negative, adjust the
            % slot after the previous active PRACH slot to be empty
            idxb = activeIndex(i-1) + 1;
            [slotSFs,aSF] = adjust(slotSFs,aSF,idxb,-totSubframes);
            % (a) Recalculate extra slots after step (b) above
            delta = startSFs(i) - slotSFs(idxa);
        end 
        % (a) Adjust the current active PRACH slot by adding extra slots
        [slotSFs,aSF] = adjust(slotSFs,aSF,idxa,delta);
    end
    
    % Adjust additional PRACH slots to ensure that the total number of 
    % subframes matches the nominal period
    if (straddleSF)
        % If the PRACH preamble straddles an extra subframe due to a
        % non-zero starting symbol, remove the extra subframe from inactive
        % PRACH slots and shorten the slot following active PRACH slots by
        % one subframe
        aSF(~activePRACHSlot) = -1;
        aSF(activeIndex + 1) = aSF(activeIndex + 1) - 1;
    else
        % Establish the number of extra subframes 'eSF' by which the 
        % combined duration of all PRACH slots exceeds the nominal period
        last = slotSFs(end) + aSF(end) + totSubframes - 1;
        nominal_last = nPRACHSlots*totSubframes - 1;
        eSF = last - nominal_last;
        % If extra subframes are present
        if (eSF > 0)
            % If the last active PRACH slot is not the last slot of the
            % nominal period
            if(activeIndex(end) < numel(aSF))
                % Shorten the PRACH slot following the last active PRACH
                % slot by 'eSF'
                idx = activeIndex(end) + 1;
                aSF(idx) =  aSF(idx) - eSF;
            else % last active PRACH slot is the last slot
                % If the current PRACH slot is in the second or subsequent
                % nominal period
                if (prach.NPRACHSlot >= nPRACHSlots)
                    % shorten the first PRACH slot by 'eSF'
                    aSF(1) =  aSF(1) - eSF;
                else % current PRACH slot is in the first nominal period
                    % The final active PRACH slot of the first nominal
                    % period will have 'eSF' extra subframes beyond the end
                    % of the nominal period. This is unavoidable, because
                    % they contain part of an active PRACH preamble. The
                    % duration of the first period is therefore the nominal
                    % period plus 'eSF'. For the second period and
                    % subsequent periods, the first slot is shortened by
                    % 'eSF' above to make space for these extra subframes,
                    % so the second period (and subsequent periods) will be
                    % of nominal length
                end
            end
        end
    end

    % Select the element of the 'aSF' vector that corresponds to the 
    % current PRACH slot
    aSF = aSF(mod(prach.NPRACHSlot,nPRACHSlots) + 1);

end

% Adjust the PRACH slot timeline 'slotSFs' by adding 'extra' slots in
% positions idx+1 and beyond, and record the number of extra subframes in
% 'eSF'
function [slotSFs,eSF] = adjust(slotSFs,eSF,idx,extra)
    eSF(idx) = extra;
    slotSFs(idx+1:end) = slotSFs(idx+1:end) + eSF(idx);
end
