function trackResults = trackingDriver( fidIn, settings, acqResults )

%==============================================================================%
%                                                                              %
%   trackResults = trackingDriver( fidIn, settings, acqResults )               %
%                                                                              %
%   Main tracking driver                                                       %
%   David S. De Lorenzo                                                        %
%   01 November 2005                                                           %
%                                                                              %
%------------------------------------------------------------------------------%
%                                                                              %
%   This routine processes one signal component - it is a scalar tracking      %
%   architecture which reads & processes the entire data file.  The            %
%   basic time-step unit of this tracking receiver is the millisecond;         %
%   the millisecond unit is hard-coded into the fundamental architecture       %
%   of the receiver, and dictates how it reads & processes all signals.        %
%                                                                              %
%   This function loops over milliseconds and forms two accumulation sums      %
%   (for early, prompt, and late code-phases) - one for the first part of      %
%   the millisecond until code rollover occurs, and one for the second part    %
%   of the millisecond until the millisecond rollover occurs.  The tracking    %
%   loops are called at the end of the millisecond, at which point the loop    %
%   filters may (if reaching the scheduled loop-entry condition) calculate     %
%   the carrier-phase and code-phase discriminators and update their           %
%   respective second-order tracking loop filters.                             %
%                                                                              %
%==============================================================================%
%                       GPS Software Receiver in Matlab                        %
%     Copyright (c) 2003-2017 David S. De Lorenzo / 2018-2023 Trimble Inc.     %
%==============================================================================%

%--- Assign temporary variables, making code more readable ---------------------
msToProcess   = settings.trk.msToProcess;
msToSkip      = settings.msToSkip;
samplesToSkip = settings.extraSamplesToSkip;
samplesPer1ms = settings.samplesPer1ms;
timePer1ms    = settings.timePer1ms;
chipsPer1ms   = settings.chipsPer1ms;
dTau          = settings.DLL.correlatorSpacingC;

%=== Initialize the active tracking channel structure ==========================
channel = trackingInitChannelStruct(settings, acqResults);

%=== Initialize the tracking results structure =================================
trackResults = trackingInitTrackingStruct(msToProcess, channel.prn);

%=== Initialize storage for detailed C/A code correlation ======================
if (settings.trk.plotCorrelationPeak == true)
    correlationPeakCodeVec = settings.trk.correlationPeakCodeVec;
    trackResults.correlationFunction = ...
        zeros(length(correlationPeakCodeVec), msToProcess);
    channel.correlationFunction1 = ...
        complex(zeros(1, length(correlationPeakCodeVec)));
    channel.correlationFunction2 = ...
        complex(zeros(1, length(correlationPeakCodeVec)));
end

%--- Store the code sequence in a temporary variable ---------------------------
codeChips = channel.codeChips;

%--- Set up progress indicator variables ---------------------------------------
progressCounter = 1;
progressPercent = 0:10:100;

%--- Seek forward to the specified file position pointer location --------------
placeFilePositionPointer(fidIn, msToSkip, samplesToSkip, settings);

%--- Start time and message ----------------------------------------------------
startTime = datetime('now');
fprintf('\nTracking PRN %d at %s\n', channel.prn, startTime);

%==============================================================================%
%                                                                              %
%  Perform code and carrier tracking for the required number of 1 msec epochs  %
%                                                                              %
%==============================================================================%
while (channel.msCount < msToProcess)
    
    %=== Read one millisecond of the signal ====================================
    data = readDataFromFile(fidIn, samplesPer1ms, settings);
    
    %--- Increment the millisecond counter -------------------------------------
    channel.msCount = channel.msCount + 1;
    
    %--- Determine the code indices for this millisecond -----------------------
    %    Account for the fractional sample in each ~1ms block
    %    caused by reading a whole (integer) number of data
    %    samples for each millisecond of receiver time.
    %    codePts1 - code phase indices until the code rollover
    %    codePts2 - code phase indices until the msec rollover
    codePts = channel.codePts - channel.codePhase ...
              + channel.extraCodeRollover * channel.codeFreq * timePer1ms ...
              + settings.extraTimePer1ms * settings.codeFreq * channel.msCount;
    codePts1 = codePts(1:find(codePts < 0, 1, 'last'));
    codePts2 = codePts(find(codePts > 0, 1, 'first'):end);
    
    %--- Store the number of samples before and after the code rollover --------
    numPts1 = length(codePts1);
    numPts2 = length(codePts2);
    
    if (numPts1 == 0)
        warning('** Abnormal exit from trackingDriver **');
        fprintf('   Most likely loss-of-lock condition\n\n');
        return
    end
    
    %==========================================================================%
    %  Processing for the first part of the millisecond until code rollover    %
    %==========================================================================%
    
    %--- Determine the value of secondary modulation on the primary code -------
    secondaryCodeVal = settings.secondarySeq(channel.databitCounter);
    
    %--- Rotate the stored zero-phase I & Q mixing vector (complex) ------------
    IQ = channel.IQ(1:numPts1) * exp(-1i * channel.carrierPhase);
    
    %--- Remove the carrier from the signal (complex) --------------------------
    %    Carrier wipeoff moves the signal to baseband.
    dataBaseband1 = data(1:numPts1) .* IQ;
    
    %--- Multiply by the secondary code value (complex) ------------------------
    dataBaseband1 = secondaryCodeVal * dataBaseband1;
    
    %--- For the first part of the millisecond (until code rollover) -----------
    %    Update the early, prompt, and late code replicas.
    replicaEarly  = codeChips(floor(mod(codePts1+dTau, chipsPer1ms)) + 1);
    replicaPrompt = codeChips(floor(mod(codePts1     , chipsPer1ms)) + 1);
    replicaLate   = codeChips(floor(mod(codePts1-dTau, chipsPer1ms)) + 1);
    
    %--- Do the correlation with the local code replicas (complex) -------------
    channel.correlators1.Early  = dataBaseband1 * replicaEarly.';
    channel.correlators1.Prompt = dataBaseband1 * replicaPrompt.';
    channel.correlators1.Late   = dataBaseband1 * replicaLate.';
    
    %--- Accumulate the second-half correlation from the previous millisecond --
    %    with the first-half correlation from this millisecond.
    channel.correlators1.Early  = channel.correlators1.Early ...
                                  + channel.correlators2.Early;
    channel.correlators1.Prompt = channel.correlators1.Prompt ...
                                  + channel.correlators2.Prompt;
    channel.correlators1.Late   = channel.correlators1.Late ...
                                  + channel.correlators2.Late;
    
    %--- Compute the correlation function in the viscinity of the C/A peak -----
    if (settings.trk.plotCorrelationPeak == true)
        %--- Code replica ------------------------------------------------------
        detailedReplica = ...
            codeChips(floor(mod(repmat(codePts1,length(correlationPeakCodeVec),1) - ...
                                repmat(correlationPeakCodeVec,1,length(codePts1)), ...
                                chipsPer1ms)) + 1);
        %--- Correlation -------------------------------------------------------
        channel.correlationFunction1 = dataBaseband1 * detailedReplica.';
        %--- Accumulation ------------------------------------------------------
        channel.correlationFunction1 = channel.correlationFunction1 ...
                                       + channel.correlationFunction2;
    end
    
    %--- Advance the carrier-phase to the code rollover ------------------------
    channel.carrierPhaseInt = ...
        channel.carrierPhaseInt ...
        + (2*pi*numPts1*channel.carrierFreq / settings.samplingFreq);
    channel.carrierPhase = mod(channel.carrierPhaseInt, 2*pi);
    
    %--- Increment the data bit counter ----------------------------------------
    channel.databitCounter = channel.databitCounter + 1;
    
    %--- Reset the databit counter for bit rollover ----------------------------
    %    or during the pull-in mode.
    if ((channel.databitCounter > settings.msPerDataBit) ...
     || (channel.msCount <= (settings.trk.pullInTimeMs+1)))
        channel.databitCounter = 1;
    end
    
    %==========================================================================%
    %  Processing for the second part of the millisecond until msec rollover   %
    %==========================================================================%
    
    %--- Determine the value of secondary modulation on the primary code -------
    secondaryCodeVal = settings.secondarySeq(channel.databitCounter);
    
    %--- Rotate the stored zero-phase I & Q mixing vector (complex) ------------
    IQ = channel.IQ(1:numPts2) * exp(-1i * channel.carrierPhase);
    
    %--- Remove the carrier from the signal (complex) --------------------------
    %    Carrier wipeoff moves the signal to baseband.
    dataBaseband2 = data(numPts1+1:end) .* IQ;
    
    %--- Multiply by the secondary code value (complex) ------------------------
    dataBaseband2 = secondaryCodeVal * dataBaseband2;
    
    %--- For the second part of the millisecond (until msec rollover) ----------
    %    Update the early, prompt, and late code replicas.
    replicaEarly  = codeChips(floor(mod(codePts2+dTau, chipsPer1ms)) + 1);
    replicaPrompt = codeChips(floor(mod(codePts2     , chipsPer1ms)) + 1);
    replicaLate   = codeChips(floor(mod(codePts2-dTau, chipsPer1ms)) + 1);
    
    %--- Do the correlation with the local code replicas (complex) -------------
    channel.correlators2.Early  = dataBaseband2 * replicaEarly.';
    channel.correlators2.Prompt = dataBaseband2 * replicaPrompt.';
    channel.correlators2.Late   = dataBaseband2 * replicaLate.';
    
    %--- Compute the correlation function in the viscinity of the C/A peak -----
    if (settings.trk.plotCorrelationPeak == true)
        %--- Code replica ------------------------------------------------------
        detailedReplica = ...
            codeChips(floor(mod(repmat(codePts2,length(correlationPeakCodeVec),1) - ...
                                repmat(correlationPeakCodeVec,1,length(codePts2)), ...
                                chipsPer1ms)) + 1);
        %--- Correlation -------------------------------------------------------
        channel.correlationFunction2 = dataBaseband2 * detailedReplica.';
    end
    
    %--- Advance the carrier-phase to the next millisecond epoch ---------------
    channel.carrierPhaseInt = ...
        channel.carrierPhaseInt ...
        + (2*pi*numPts2*channel.carrierFreq / settings.samplingFreq);
    channel.carrierPhase = mod(channel.carrierPhaseInt, 2*pi);
    
    %==========================================================================%
    %  Call the tracking loops                                                 %
    %==========================================================================%
    
    %--- Detect code-phase rollover --------------------------------------------
    if (codePts1(end) < -3)
        
        %--- Increment the extra code rollover counter -------------------------
        channel.extraCodeRollover = channel.extraCodeRollover + 1;
        
        %--- Replace the second-half correlator totals -------------------------
        channel.correlators2.Early  = channel.correlators1.Early;
        channel.correlators2.Prompt = channel.correlators1.Prompt;
        channel.correlators2.Late   = channel.correlators1.Late;
        
        %--- Replace the detailed correlation function total -------------------
        if (settings.trk.plotCorrelationPeak == true)
            channel.correlationFunction2 = channel.correlationFunction1;
        end
        
        %--- Decrement the millisecond counter ---------------------------------
        channel.msCount = channel.msCount - 1;
        
    %--- Otherwise call the tracking loop function as normal -------------------
    else
        
        %--- Advance the code-phase to the next millisecond epoch --------------
        channel.codePhase = channel.codePhase ...
                            + (settings.codeFreq-channel.codeFreq) * timePer1ms;
        
        %=== Perform code and carrier tracking loops ===========================
        [channel,trackResults] = trackingLoops(settings, channel, trackResults);
        
    end
    
    %--- Display progress update every 10% -------------------------------------
    while (channel.msCount/msToProcess) > (progressPercent(progressCounter)/100)
        fprintf('    Processing %2d%% complete\n', ...
                progressPercent(progressCounter));
        progressCounter = progressCounter + 1;
    end
    
end % while (channel.msCount < msToProcess)

%=== Calculate and save additional information =================================
trackResults = trackingPostProcessing(settings, trackResults);

%--- Stop time and message -----------------------------------------------------
finishTime = datetime('now');
fprintf('    Processing 100%% complete\n');
fprintf('  Elapsed time = %6.1f sec\n', seconds(finishTime - startTime));
fprintf('    Final C/No = %4.1f dB-Hz\n', trackResults.CNoFinal);

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