function [acqResults, resultsNoncoherent] = ...
                  acquisitionIntegrationEngine( fidIn, prn, dopplerEstimate, ...
                                                msCount, settings )

%==============================================================================%
%                                                                              %
%   [acqResults, resultsNoncoherent] = ...                                     %
%       acquisitionIntegrationEngine( fidIn, prn, dopplerEstimate, ...         %
%                                     msToSkip, settings )                     %
%                                                                              %
%   Main acquisition engine for one satellite                                  %
%   David S. De Lorenzo                                                        %
%   28 January 2008                                                            %
%                                                                              %
%------------------------------------------------------------------------------%
%                                                                              %
%   The acquisition engine does coherent and non-coherent integration on       %
%   GPS C/A-code signals, with parameters passed in from a calling routine.    %
%   First, there is coherent integration for 'msToProcessCoherent'             %
%   milliseconds (this must be <= 20ms).  Second, there is a march-ahead       %
%   in the input data file to finish off an entire 20ms navigation data        %
%   bit.  Third, there is non-coherent integration for 'noncoherentBlocks'     %
%   number of coherent blocks.  Finally, there is some logic to load           %
%   acquisition results into the appropriate output variables for return       %
%   to the calling routine.                                                    %
%                                                                              %
%==============================================================================%
%                       GPS Software Receiver in Matlab                        %
%     Copyright (c) 2003-2017 David S. De Lorenzo / 2018-2023 Trimble Inc.     %
%==============================================================================%

%--- Assign temporary variables, making code more readable ---------------------
msToProcessCoherent  = settings.acq.msToProcessCoherent;
maxCarrierPhaseError = settings.acq.maxCarrierPhaseError;
dopplerSearchRange   = settings.acq.dopplerSearchRange;
samplesPer1ms        = settings.samplesPer1ms;
timePer1ms           = settings.timePer1ms;
chipsPer1ms          = settings.chipsPer1ms;

%--- Initialize the acquisition results structure and store the prn number -----
acqResults.prn = prn;

%--- PRN code chip sequence, values of +1 and -1 -------------------------------
codeChips = codeGenerateMASTER(settings.signalType, prn);

%--- Repeat the last chip to protect against index roundoff errors -------------
codeChips = [codeChips codeChips(end)];

%--- Phase increment for each ~1ms block ---------------------------------------
phasePer1ms = 2 * pi * timePer1ms;
phasePer1ms = mod(phasePer1ms, 2*pi);

%--- Doppler frequency search spacing in Hz ------------------------------------
dopplerSearchStep = 2 * 1000 * maxCarrierPhaseError / msToProcessCoherent;

%--- Number of frequency bins for the given Doppler search range ---------------
numberOfDopplerBins = ...
    1 + 2*ceil(dopplerSearchRange / (2*dopplerSearchStep));

%--- Nominal Doppler frequency search vector -----------------------------------
dopplerSearchNominal = linspace(-dopplerSearchRange / 2, ...
                                 dopplerSearchRange / 2, ...
                                numberOfDopplerBins);

%--- Doppler frequency vector for this PRN -------------------------------------
%    Note: This provides the option of passing a Doppler frequency estimate.
acqResults.dopplerFreqVector = dopplerEstimate + dopplerSearchNominal;

%=== Initialize correlation results ============================================
resultsCoherent    = zeros(numberOfDopplerBins, samplesPer1ms);
resultsNoncoherent = zeros(numberOfDopplerBins, samplesPer1ms);

%=== Loop over non-coherent block number =======================================
%    This combines more than 1 coherent integration interval.
for noncoherentBlockNumber = 1:settings.acq.noncoherentBlocks
    
    %--- Reset coherent integration --------------------------------------------
    resultsCoherent(:,:) = 0;
    
    %=== Loop over coherent integration ========================================
    %    This advances time in 1ms chunks.
    for coherentMsNumber = 1:msToProcessCoherent
        
        %--- Increment the millisecond counter ---------------------------------
        msCount = msCount + 1;
        
        %=== Read in ~1ms of data from file ====================================
        data = readDataFromFile(fidIn, samplesPer1ms, settings);
        
        %--- Absolute time/phase of each sample point --------------------------
        timePts  = settings.timePts  + ((msCount-1) * timePer1ms);
        phasePts = settings.phasePts + ((msCount-1) * phasePer1ms);
        phasePts = mod(phasePts, 2*pi);
        
        %=== Loop over Doppler frequency =======================================
        for dopplerSearchBin = 1:numberOfDopplerBins
            
            %--- Set signal frequencies ----------------------------------------
            dopplerFreq = acqResults.dopplerFreqVector(dopplerSearchBin);
            carrierFreq = dopplerFreq + settings.intermediateFreq;
            codeFreq    = settings.codeFreq ...
                          + dopplerFreq * settings.carrierAidingSF;
            
            %--- Remove the carrier from the signal (complex) ------------------
            %    Carrier wipeoff moves the signal to baseband.
            dataBaseband = data .* exp(-1i * carrierFreq * phasePts);
            
            %--- Convert the signal to frequency domain via FFT (complex) ------
            dataBasebandFFT = fft(dataBaseband);
            
            %--- Construct the code replica ------------------------------------
            replicaCodeSequence = ...
                codeChips(floor(mod(codeFreq*timePts, chipsPer1ms)) + 1);
            
            %--- Convert the code replica to frequency domain via FFT (complex)-
            replicaCodeSequenceFFT = fft(replicaCodeSequence);
            
            %--- Multiplication in the frequency domain (complex) --------------
            %    Convolution in the time domain is equivalent to
            %    multiplication in the frequency domain.
            convFreqDomain = dataBasebandFFT .* conj(replicaCodeSequenceFFT);
            
            %--- Perform inverse FFT to return to the time domain (complex) ----
            convTimeDomain = ifft(convFreqDomain);
            
            %--- Accumulate correlation results (complex) ----------------------
            resultsCoherent(dopplerSearchBin,:) = ...
                resultsCoherent(dopplerSearchBin,:) + convTimeDomain;
            
        end % loop over Doppler frequency
        
    end % loop over coherent integration
    
    %--- Combine coherent sum with non-coherent sum (complex --> real) ---------
    resultsNoncoherent = resultsNoncoherent + abs(resultsCoherent);
    
    %--- Complete the passage through a navigation data bit --------------------
    %    Note that file traversal is done in this way, rather than
    %    with an 'fseek' call, since this method handles data samples
    %    that are of fractional byte in size.
    for extraMs = (msToProcessCoherent+1):settings.msPerDataBit
        
        %--- Increment the millisecond counter ---------------------------------
        msCount = msCount + 1;
        
        %=== Read in ~1ms of data from file ====================================
        readDataFromFile(fidIn, samplesPer1ms, settings);
        
    end % loop over extraMs
    
end % loop over non-coherent block number

%=== Post-processing to calculate acquisition results ==========================
acqResults = acquisitionPostProcessing(acqResults, settings, ...
                                       resultsNoncoherent);

return
%===============================================================================