GPS Receiver Acquisition and Tracking
This example shows how to acquire and track Global Positioning System (GPS) signals. The example supports these signals.
GPS L1 C/A [1]
GPS L1C [2]
GPS L5 [3]
Introduction
All the GPS signals that work on the principle of code division multiple access (CDMA) use similar acquisition and tracking algorithms. Acquisition is a process that detects all visible satellites and estimates the coarse values of frequency offset (that includes Doppler shift) and code phase offset for these visible satellites. Using the values from acquisition, a tracking module fine-tunes the frequency offset and code-phase offset for the visible satellites. Acquisition is typically the first step in a GPS receiver. Use the information from acquisition to perform parallel receiver operations on each of the visible satellites, as shown in the figure. For a given visible satellite, acquisition is a one-time operation, unless some signal loss occurs, whereas tracking is a continuous process to keep a track of changes to frequency offset and code phase offset.
In the example, generate GPS baseband waveform for satellites of your choice, perform acquisition to search for visible satellites in the signal, and track the visible satellites as shown in this figure.
For detailed information on the acquisition and tracking algorithm, see the GPS Receiver Acquisition and Tracking Using C/A-Code example.
Set Initial Parameters
Specify all the parameters that control the example workflow.
First, specify the signal type.
signalType = "GPS L1CD"
signalType = "GPS L1CD"
Optionally, you can write the generated waveform and tracking output to a file by specifying writeWaveformToFile
and writeTrackedSignalToFile
, respectively, as true
.
writeWaveformToFile = false; writeTrackedSignalToFile = false;
Specify that you want to perform acquisition on only visible satellites. To instead perform acquisition on all GPS satellites, specify acquireActiveSatellitesOnly
as false
, but note that choosing to acquire all GPS satellites can cause memory issues, depending on your available system memory.
acquireActiveSatellitesOnly = true;
Specify the number of visible satellites.
numSat = 4;
Specify the pseudo-random noise (PRN) numbers of the visible satellites.
PRNIDs = [7; 11; 20; 28];
Specify the number of data bits to process in this example. For a long-running simulation, set numDataBits
to an appropriate higher value. For example, transmitting all of the information for a GPS L1C signal requires 8 frames of 1800 bits each, which is a total of 14400 bits. Because running such a simulation takes a long time, set this parameter to a smaller value to validate the example results.
numDataBits = 300;
Specify the Doppler, delay, and signal-to-noise ratio (SNR) for each of the visible satellites.
frequencyOffset = [-3589; 4256; -1563; 2666]; % In Hz sigdelay = [300.34; 587.21; 425.89; 312.88]; % Number of code chips delay SNRs = [-25; -25.5; -26.5; -26]; % In dB
Set the intermediate frequency and sampling rate of the signal.
IntermediateFrequency = 0; % In Hz SampleRate = 20e6; % In samples/sec
While performing acquisition, set the frequency step size to a real positive number. When you set this value to -1, the example considers these default frequency step size, depending on the signal type.
"GPS C/A", "GPS L5I", "GPS L5Q" — 500 Hz
"GPS L1CD" — 50 Hz
acquisitionFrequencyStep = -1; % Default
Set the loop noise bandwidths of the phase-locked loop (PLL), frequency-locked loop (FLL), and delay-locked loop (DLL). Higher bandwidth allows more noise through a loop, but enables it to track fast-varying parameters. Lower bandwidth allows less noise through the loop, but the lock will be lost if tracking parameters vary quickly. For open sky environments where the receiver is moving at high speeds, you can expect low noise and fast changing parameters, so set the noise bandwidths to higher values. In high-noise environments, such as indoors, reduce the noise bandwidths to increase receiver sensitivity. In this example, use moderate values of loop bandwidths because the noise is moderate in the default setting of the example.
PLLNoiseBandwidth = 90; % In Hz FLLNoiseBandwidth = 4; % In Hz DLLNoiseBandwidth = 1; % In Hz
Set the seed for generating reproducible waveform.
Seed = 73;
Configure Simulation
Configure the simulation by using the parameters specified in the previous section.
First, configure initial set of parameters.
isl1c = strcmp(signalType,"GPS L1CD"); % Configure various parameters based on the signalType. switch(signalType) case "GPS C/A" numBitsPerStep = 1; numCodeBlocksPerBit = 20; chipRate = 1.023e6; samplesPerCodeBlock = SampleRate*1e-3; ibranch = "P+D"; qbranch = "C/A+D"; navDataType = "LNAV"; defaultAcqFqyStep = 500; case "GPS L1CD" numBitsPerStep = 2; numCodeBlocksPerBit = 1; chipRate = 1.023e6; samplesPerCodeBlock = SampleRate*10e-3; ibranch = "l1C"; qbranch = "None"; navDataType = "CNAV2"; defaultAcqFqyStep = 50; case {"GPS L5I" "GPS L5Q"} numBitsPerStep = 1; numCodeBlocksPerBit = 10; chipRate = 10.23e6; samplesPerCodeBlock = SampleRate*1e-3; ibranch = "I5-of-L5"; qbranch = "Q5-of-L5"; navDataType = "L5"; defaultAcqFqyStep = 500; otherwise error("Invalid signalType.") end numCodeBlocks = numDataBits*numCodeBlocksPerBit; numSteps = numDataBits/numBitsPerStep; numCodeBlocksPerStep = numCodeBlocksPerBit*numBitsPerStep; if acquisitionFrequencyStep == -1 % Set default value acqfqystep = defaultAcqFqyStep; else acqfqystep = acquisitionFrequencyStep; end if writeWaveformToFile == true % Initialize the baseband file writer object bbwriter = comm.BasebandFileWriter("gpsWaveform.bb",SampleRate,IntermediateFrequency); end
Initialize the waveform generation object based on the initial configuration.
gpswavegen = HelperGPSWaveformGenerator(PRNID=PRNIDs, ... InPhaseSignalType=ibranch,QuadraturePhaseSignalType=qbranch, ... SignalToNoiseRatio=SNRs,SignalDelay=sigdelay/chipRate, ... FrequencyOffset=frequencyOffset,IntermediateFrequency=IntermediateFrequency, ... SampleRate=SampleRate,RandomStream="mt19937ar with seed",Seed=Seed)
gpswavegen = HelperGPSWaveformGenerator with properties: Transmitter Properties PRNID: [4x1 double] InPhaseSignalType: "L1C" QuadraturePhaseSignalType: "None" InitialTime: 0 Signal Properties SampleRate: 20000000 IntermediateFrequency: 0 Channel Properties DisableImpairments: false SignalToNoiseRatio: [4x1 double] SignalDelay: [4x1 double] FrequencyOffset: [4x1 double] RandomStream: "mt19937ar with seed" Seed: 73
Configure the receiver acquisition object
gsa = gnssSignalAcquirer(GNSSSignalType=signalType, ... FrequencyResolution=acqfqystep, ... IntermediateFrequency=IntermediateFrequency, ... SampleRate=SampleRate)
gsa = gnssSignalAcquirer with properties: GNSSSignalType: "GPS L1CD" SampleRate: 20000000 IntermediateFrequency: 0 FrequencyRange: [-10000 10000] FrequencyResolution: 50 DetectionThresholdFactor: 1.9000
% If CNAV, encode it with convolutional encoder % Initialize the trellis for convolutional encoder trellis = poly2trellis(7,["1+x+x^2+x^3+x^6" "1+x^2+x^3+x^5+x^6"]); cenc = comm.ConvolutionalEncoder(TrellisStructure = trellis, ... TerminationMethod = "Continuous"); cfg = cell(numSat,1); cfg{1} = HelperGPSNavigationConfig(SignalType=navDataType, ... L1CTOI=60); tempData = HelperGPSNAVDataEncode(cfg{1}); if any(strcmp(navDataType,["CNAV" "L5"])) tempData = cenc(tempData); reset(cenc); end transmittedBits = [tempData zeros(size(tempData,1),numSat-1)]; for isat = 2:numSat cfg{isat} = HelperGPSNavigationConfig(SignalType=navDataType, ... L1CTOI=60); navdata = HelperGPSNAVDataEncode(cfg{isat}); if any(strcmp(navDataType,["CNAV" "L5"])) transmittedBits(:,isat) = cenc(navdata); reset(cenc); else transmittedBits(:,isat) = navdata; end end
Acquire GPS Signals
Generate a waveform that is adequate for acquisition, and use the receiver acquisition object to acquire it.
% Initialize data bits for the waveform b = transmittedBits(1:numBitsPerStep,:); % Generate waveform for those data bits w = gpswavegen(b); if writeWaveformToFile == true bbwriter(w) end % Perform acquisition if acquireActiveSatellitesOnly == true prntable = gsa(w(end-2*samplesPerCodeBlock+1:end),PRNIDs) else % Searching for all satellites requires more system memory prntable = gsa(w(end-2*samplesPerCodeBlock+1:end),1:32) end
prntable=4×4 table
PRNID FrequencyOffset CodePhaseOffset IsDetected
_____ _______________ _______________ __________
7 -3600 300.35 true
11 4250 587.2 true
28 2650 312.88 true
20 -1550 425.87 true
PRNIDsToSearch = prntable(prntable(:,4).IsDetected==1,1).PRNID.'; % Get row vector doppleroffsets = prntable(prntable(:,4).IsDetected==1,2).FrequencyOffset; codephoffsets = prntable(prntable(:,4).IsDetected==1,3).CodePhaseOffset; numdetectsat = length(PRNIDsToSearch); disp("The detected satellite PRN IDs: " + num2str(PRNIDsToSearch))
The detected satellite PRN IDs: 7 11 28 20
Track GPS Signals
Create and configure the signal tracking object. Continue to generate the waveform and track the signal in a loop after acquisition is complete. Because this example assumes that the satellites do not change while the simulation is in progress, the signal tracking object can handle multiple satellites.
if isempty(PRNIDsToSearch) % Because no satellites are detected, stop the simulation return end carrierCodeTrack = gnssSignalTracker; carrierCodeTrack.GNSSSignalType = signalType; carrierCodeTrack.SampleRate = SampleRate; carrierCodeTrack.IntermediateFrequency = IntermediateFrequency; carrierCodeTrack.PLLNoiseBandwidth = PLLNoiseBandwidth; carrierCodeTrack.FLLNoiseBandwidth = FLLNoiseBandwidth; carrierCodeTrack.DLLNoiseBandwidth = DLLNoiseBandwidth; carrierCodeTrack.PRNID = PRNIDsToSearch; carrierCodeTrack.InitialFrequencyOffset = doppleroffsets; carrierCodeTrack.InitialCodePhaseOffset = codephoffsets; longDisplay(carrierCodeTrack)
gnssSignalTracker with properties: GNSSSignalType: "GPS L1CD" SampleRate: 20000000 IntermediateFrequency: 0 Initialization from acquisition PRNID: [7 11 28 20] InitialCodePhaseOffset: [4x1 double] InitialFrequencyOffset: [4x1 double] Phase Locked Loop (PLL) DisablePLL: 0 PLLOrder: 2 PLLNoiseBandwidth: 90 Frequency Locked Loop (FLL) DisableFLL: 0 FLLOrder: 1 FLLNoiseBandwidth: 4 Delay Locked Loop (DLL) DLLOrder: 1 DLLNoiseBandwidth: 1
Initialize variables for storing the output from the tracking object.
% Initialize all the properties which must be accumulated. [accuph, accufqy, accufqyerr, accupherr, ... accudelay, accudelayerr, accuintegwave] = ... deal(zeros(numCodeBlocks,numdetectsat)); % Each column represents data from a satellite
Perform initial tracking of the generated waveform, and store the values.
[trackedWave,trackinfo] = carrierCodeTrack(w); tridx = 1:numCodeBlocksPerStep; accuintegwave(tridx,:) = trackedWave; accuph(tridx,:) = trackinfo.PhaseEstimate; accupherr(tridx,:) = trackinfo.PhaseError; accufqy(tridx,:) = trackinfo.FrequencyEstimate; accufqyerr(tridx,:) = trackinfo.FrequencyError; accudelay(tridx,:) = trackinfo.DelayEstimate; accudelayerr(tridx,:) = trackinfo.DelayError;
Track the signals in a loop.
for ii = 2:numSteps % Waveform generation b = transmittedBits((ii-1)*numBitsPerStep+1:ii*numBitsPerStep,:); w = gpswavegen(b); if writeWaveformToFile == true bbwriter(w); end % Track at receiver [trackedWave,trackinfo] = carrierCodeTrack(w); % Store tracking output tridx = (ii-1)*numCodeBlocksPerStep + (1:numCodeBlocksPerStep); accuintegwave(tridx,:) = trackedWave; accuph(tridx,:) = trackinfo.PhaseEstimate; accupherr(tridx,:) = trackinfo.PhaseError; accufqy(tridx,:) = trackinfo.FrequencyEstimate; accufqyerr(tridx,:) = trackinfo.FrequencyError; accudelay(tridx,:) = trackinfo.DelayEstimate; accudelayerr(tridx,:) = trackinfo.DelayError; end trackedOutput = accuintegwave(1:tridx(end),:); trackedOutput = trackedOutput/rms(trackedOutput(:)); % Normalize tracking output if writeWaveformToFile == 1 release(bbwriter); end % Plot constellation after tracking is complete scatterplot(trackedOutput(:))
Further Exploration
Using this example, you can store the generated waveform to a file and use those samples in your receiver chain to test your receiver.
You can also write the output of the tracker to a file for further processing. Store the tracker output as a signed, fixed-point number.
if writeTrackedSignalToFile == true % Initialize the number of bits for the integer (a) and fractional (b) % parts of the fixed point number. Ensure that a+b is always 7, so as % to store the signal in int8 format. a = 1; % Number of bits for integer part b = 6; % Number of bits for fractional part % Convert the tracked output to integer values. intTrackedOutput = round(trackedOutput*2^b); % Store the real and imaginary components of the integer-tracked signal % as different matrices for the purpose of converting them to % fixed-point numbers independently. tempTrackedSignal = zeros(size(intTrackedOutput,1),size(intTrackedOutput,2),2); tempTrackedSignal(:,:,1) = real(intTrackedOutput); tempTrackedSignal(:,:,2) = imag(intTrackedOutput); signOfTrackedSignal = int8(sign(tempTrackedSignal)); % Store only the LSBs of the absolute value. Handle the sign separately. lsbOfTrackedSignal = bitand(int32(abs(tempTrackedSignal)),2^(a+b)-1); % Apply the sign to the signal. tempFixedPointSignal = int8(lsbOfTrackedSignal).*signOfTrackedSignal; % Create a fixed-point, complex signal in int8 format. trackedSignal = complex(tempFixedPointSignal(:,:,1),tempFixedPointSignal(:,:,2)); save trackedSignal trackedSignal end
Supporting Files
This example uses these data and helper files.
gpsAlmanac.txt
— Almanac data file downloaded from Navcen websiteHelperGPSNAVDataEncode.m
— Encode navigation data from configuration object into bitsHelperGPSNavigationConfig.m
HelperGPSWaveformGenerator.m
— Generate GPS waveformL1CLDPCParityCheckMatrices.mat
— LDPC parity check matrix for encoding L1C data
References
[1] IS-GPS-200, Rev: N. NAVSTAR GPS Space Segment/Navigation User Segment Interfaces. Aug 22, 2022; Code Ident: 66RP1.
[2] IS-GPS-800, Rev: J. NAVSTAR GPS Space Segment/User segment L1C Interfaces. Aug 22, 2022; Code Ident: 66RP1.
[3] IS-GPS-705, Rev: J. NAVSTAR GPS Space Segment/User segment L1C Interfaces. Aug 22, 2022; Code Ident: 66RP1.