function cellDetectorConsts = ltehdlDownlinkSyncDemodConstants(sampleTime)
    %cellDetectorConsts = ltehdlDownlinkSyncDemodConstants(sampleTime)
    %   Returns a structure, cellDetectorConfig, containing constants and lookup
    %   tables used by the ltehdlDownlinkSyncDemod model reference.
    %   sampleTime specifies the Simulink sample time of the input to the
    %   detctor. Note that the actual sample rate of the input is assumed
    %   to be 30.72MHz.
    
    %   Copyright 2018 The MathWorks, Inc.
    
    PSSThreshold_dB = -6.4;
    SSSThreshold_dB = -6.4;
    
    % PSS and SSS retry attempts
    PSSAttempts = 2;
    SSSAttempts = 4;
    
    % Scale the signal power measurement by this amount to generate a threshold.
    PSSThresholdScalingFactor = 10^(PSSThreshold_dB/10);
        
    SSSThresholdScalingFactor = 10^(SSSThreshold_dB/10);
    
    % Generate PSS sequences in the time domain.
    w0 = flipud(conj(timeDomainPSS(0,128)));
    w1 = flipud(conj(timeDomainPSS(1,128)));
    w2 = flipud(conj(timeDomainPSS(2,128)));
    
    % Normalize energy of each sequence to 1.
    w0 = w0/sqrt(w0'*w0);
    w1 = w1/sqrt(w1'*w1);
    w2 = w2/sqrt(w2'*w2);
    
    % Coefficient quantization
    coeffWL = 8;
    wcombined = fi([w0 w1 w2],1,coeffWL);
    w0 = double(wcombined(:,1));
    w1 = double(wcombined(:,2));
    w2 = double(wcombined(:,3));
    PSSNumerictype = wcombined.numerictype;
    
    % Finally create PSS filter weight row vectors
    PSS = [w0 w1 w2].';
    
    % Generate possible SSS candidates table, making first dimension a power of 2
    % for more efficient resource mapping.
    SSS         = zeros(64,1008);
    SSS(1:62,:) = generateSSSCandidates();
    SSSNormFactor = 1 / sqrt(SSS(:,1)'*SSS(:,1));
    
    % Decimating filter chain parameters.
    [decimFilters, decimFiltersGroupDelay] = designDecimatingFilterChain();
    
    % Extra delay to add to filter chain input to make it up to a multiple of 16
    decimFiltersExtraInputDelay = 16 - mod(decimFiltersGroupDelay,16);
    decimFiltersTransientLength = (decimFiltersExtraInputDelay + decimFiltersGroupDelay)/16;
    
    cellDetectorConsts = struct( ...
        'PSS', PSS, ...
        'SSS', SSS, ...
        'PSSAttempts', PSSAttempts, ...
        'SSSAttempts', SSSAttempts, ...
        'PSSNT', PSSNumerictype, ...
        'PSSThreshold_dB', PSSThreshold_dB, ...
        'PSSThresholdScalingFactor', PSSThresholdScalingFactor, ...
        'PSSSharingFactor', 32, ...
        'PSSStreamSyncBufferDepth', 4, ...
        'SSSNormFactor', SSSNormFactor, ...
        'SSSThreshold_dB', SSSThreshold_dB, ...
        'SSSThresholdScalingFactor', SSSThresholdScalingFactor, ...
        'Ts',  sampleTime, ...
        'decimFilters', decimFilters, ...
        'decimFiltersExtraInputDelay', decimFiltersExtraInputDelay, ...
        'decimFiltersTransientLength', decimFiltersTransientLength);
    
end

%--------------------------------------------------------------------------------------------------

function Pt = timeDomainPSS(NCellID,nFFT)
    % Function to generate time-domain PSS sequence from frequency-domain ZC sequence
    %   Inputs:
    %      NCellID : Cell ID {0,1,2}
    %      nFFT    : FFT length {128, 256, 512, 1024, 2048}
    %   Outputs
    %      Pt      : Time-domain PSS signal
    
    % Number of zeros to pad either side of the PSS sequence
    pad = (nFFT - 62)/2;
    
    % Map PSS into central subcarriers (use ltePSS to generate
    % frequency-domain PSS sequence
    pFF = [zeros(pad,1); ltePSS(struct('NCellID',NCellID,'NSubframe',0,...
        'DuplexMode','FDD')); zeros(pad,1)];
    
    % Shift second half of PSS to free DC subcarrier
    pFF(nFFT/2+2:nFFT/2+32) = pFF(nFFT/2+1:nFFT/2+31);
    
    % NULL DC subcarrier
    pFF(nFFT/2+1) = 0;
    
    % Create time-domain PSS seq by performing shifted IFFT
    Pt = ifft(fftshift(pFF));
    
end

%--------------------------------------------------------------------------------------------------

function candidates = generateSSSCandidates()
    % Generate all possible SSS candidate sequences in the frequency domain.
    
    candidates = zeros(62,1008);
    
    % Subframe 0
    enbSubframe0 = struct('NCellID',1','NSubframe',0);
    enbSubframe5 = struct('NCellID',1','NSubframe',5);
    
    for i=1:504
        enbSubframe0.NCellID = i-1;
        enbSubframe5.NCellID = i-1;
        % Use LTE Toolbox to generate SSS sequence for each subframe option
        SSSseqSubframe0 = lteSSS(enbSubframe0);
        SSSseqSubframe5 = lteSSS(enbSubframe5);
        % Only store real part of samples
        % (teSSS returns complex values with imag part set to 0)
        candidates(:,i)     = real(SSSseqSubframe0);
        candidates(:,504+i) = real(SSSseqSubframe5);
    end
    
end

%--------------------------------------------------------------------------------------------------

function [decimFiltChain, groupDelay] = designDecimatingFilterChain()
    
    %        -->[cic]-->[gain]-->[droopComp]-->
    % decim:      8                   2
    
    Fpass = 1e6/2;
    
    R              = 8;                % CIC decimation factor
    Fs             = 30.72e6;
    FsOutCic       = Fs/R;
    FsOutDroopComp = FsOutCic/2;
    
    cic = dsp.CICDecimator(...
        'DecimationFactor', R,...
        'NumSections',      4);
    
    cicGainComp = dsp.FIRFilter('Numerator',1/gain(cic));
    
    cicDroopComp = dsp.CICCompensationDecimator(2, ...
        'SampleRate',            FsOutCic, ...
        'CICRateChangeFactor',   cic.DecimationFactor, ...
        'CICNumSections',        cic.NumSections, ...
        'PassbandFrequency',     Fpass, ...
        'StopbandFrequency',     FsOutDroopComp/2, ...
        'PassbandRipple',        0.1, ...
        'StopbandAttenuation',   50);
    
    decimFiltChain = cascade(cic,cicGainComp,cicDroopComp);
    
    gdCic          = cic.NumSections*(cic.DecimationFactor + 1)/2;
    gdCicDroopComp = (length(cicDroopComp.coeffs.Numerator)-1)/2;
    groupDelay     = gdCic + 8*gdCicDroopComp;
    
end
