Main Content

End-to-End NavIC Receiver

Since R2025a

This example simulates the Navigation with Indian Constellation (NavIC) signal generation and reception chain to estimate the location of the receiver. The example simulation consists of a multi-satellite scenario using a NavIC satellite RINEX (Receiver Independent Exchange) file, and calculates the Doppler shift and signal latency associated with a certain receiver position for all the satellites. The Doppler shift and latency values enable a realistic simulation by impairing the signal. The receiver modules process the ensemble of impaired signals in this order: acquisition, tracking, bit-synchronization, frame-synchronization, data decoding, and position estimation of the receiver.

Example Workflow

This section highlights the workflows that serve as the building blocks of this end-to-end chain. These points provides an overview of the waveform generation and propagation chain steps.

  1. Using a RINEX file, simulate the NavIC constellation using the satelliteScenario object.

  2. Calculate the Doppler shift and signal latency for all satellites at each of the simulation time points.

  3. Configure the parameters of navigation data object, which includes ephemerides and clock parameters, using the RINEX file.

  4. Obtain navigation data bits from a navigation data object.

  5. Generate a NavIC waveform by spreading the navigation data and then multiplex the subcarrier signals using the interplex modulation. For more information on generating a NavIC waveform and using interplex modulation, see NavIC Waveform Generation and interplexmod, respectively.

  6. Propagate the interplexed signal through a channel, which impairs each of satellite signals with the calculated Doppler shift, signal latency, and random noise.

Similarly, these are the steps of the receiver chain.

  1. Use gnssSignalAcquirer and gnssSignalTracker objects to acquire and track all transmitting satellites, respectively.

  2. Use gnssBitSynchronize to perform bit synchronization, that is, find the start position of the first valid bit in the received samples.

  3. Synchronize the subframe by matching the sync-code transmitted with each subframe.

  4. Decode the navigation data from the detected subframes.

  5. Calculate pseudoranges by estimating the transit time.

  6. Estimate the receiver position using pseudoranges and satellite positions at the transmission time.

Initialize Simulation Parameters

Initialize the relevant simulation parameters.

Check the ShowVisualization check box to show the plots in simulation. You can enable WriteWaveformToFile to save the generated complex baseband waveform to a file, which you can use later with another receiver. WaveformType specifies the band for which to generate the waveform.

ShowVisualizations = true;
WriteWaveformToFile = false;
WaveformType = "NavIC L5";

A NavIC L5 or S band subframe is 12 seconds long. Initialize the simulation data duration as the total duration of the subframes to be transmitted. Note that a NavIC receiver requires 60 seconds or more of data to estimate the receiver position. If you aim to run complete simulation to estimate the receiver's position, set the duration to 60 seconds or more, as this is the minimum required for position estimation by a NavIC receiver.

However, if you want faster execution or have limited computational resources, you can set simulationDuration to any value less than 60 seconds since the NavICreceiverPositionProperties dataset is available. This dataset already includes the necessary data for position estimation, allowing you to conduct quicker simulations. Initialize the sampling rate for baseband waveform generation.

% Set this as a multiple of 12 for a simulation longer than 60 seconds.
simulationDuration = 3; % In seconds

% Set this value to define the sample rate of the generated waveform.
fs = 10e6;            % In Hz

This example processes data in chunks of 1 millisecond (ms). In other words, the example generates waveform of 1 ms, and then processes through the receiver chain at each step.

stepTime = 1e-3;                                 % In seconds
numSteps = (simulationDuration/stepTime) + 1;

Initialize the receiver position to model the waveform propagation channel based on its location. It is crucial to model the propagation delays accurately to simulate a NavIC receiver. This example models a stationary receiver, and does not support a moving receiver.

rxlat = 28.6329; % Delhi
rxlon = 77.2195;
rxalt = 100;

As delays are modeled in the propagation channel, a Global Navigation Satellite System (GNSS) receiver receives the actual signal after some delay. The typical delay for a NavIC signal is around 120 ms. Therefore, set an appropriate receiver wait time parameter to ensure the receiver captures a meaningful signal rather than just noise.

rxWaitTime = 149; % In ms

Initialize the parameters for the receiver functions. You need at least 4 tracking channels to track the minimum number of satellites required for estimating the position of the receiver.

% Initialize maximum number of tracking channels.
maxNumTrackingChannels = 8;

% Noise bandwidth of each of the tracking loops
PLLNoiseBW = 90;            % In Hz
FLLNoiseBW = 4;             % In Hz
DLLNoiseBW = 3;             % In Hz    

% Bit synchronization parameters
numBitsForBitSync = 100;
numWaitingStepsForBitSync = 20*numBitsForBitSync;
rxcntr = 1;

Initialize the physical constants required for the simulation.

c = physconst("LightSpeed");            % Speed of light in m/sec
if strcmp(WaveformType,"NavIC L5")
    fc = 1176.45e6;                     % NavIC L5 band carrier frequency in Hz
else
    fc = 2492.028e6;                    % NavIC S band carrier frequency in Hz
end
Dt = 12;                                % Directivity of the transmit antenna in dBi
DtLin = db2pow(Dt);
Dr = 4;                                 % Directivity of the receive antenna in dBi
DrLin = db2pow(Dr);
Pt = 50;                                % Typical transmission power of a GNSS satellite in watts
k = physconst("boltzmann");             % Boltzmann constant in Joules/Kelvin
T = 300;                                % Room temperature in Kelvin
Nr = k*T*fs;                            % Thermal noise power in watts

Configure Simulation

Set up the simulation using the simulation parameters and physical constants. The diagram outlines the simulation configuration of this section.

The simulation uses RINEX files to exchange satellite navigation data from various GNSS constellations. They contain data such as the ephemeris and clock corrections for visible satellites at different times. Because a satelliteScenario simulation requires only one data point per satellite, extract only the first data point for each PRN ID from the RINEX data. Use extracted data to add satellites to the scenario and create a navigation configuration object.

simulation-config.png

Parse the RINEX file to create a configuration object for navigation data generation.

% Specify the RINEX filename.
rinexFileName = "IITK00IND_R_20243400400_01H_MN.rnx";
rinexdata = rinexread(rinexFileName);
allNavicData = rinexdata.NavIC;

% Extract the navigation parameters for the available unique satellites.
[~,satIdx] = unique(allNavicData.SatelliteID);
navicData = sortrows(allNavicData(satIdx,:),1);

% Leap second correction from NavIC to UTC time.
leapSecondCorrection = -seconds(18);
% Create a navigation data configuration object using the RINEX file.
navConfig = HelperNavICRINEX2Config(navicData);

Configure a scenario with the required navigation parameters using the corresponding RINEX file.

sc = satelliteScenario;
% Set the NavIC clock start time 
gnssStart = datetime(1999,08,22,0,0,0,TimeZone="UTC") + leapSecondCorrection;

% Ensure that the time of week (TOW) value is 1 plus a multiple of the
% number of subframes so that the data for subframe 1 is always generated
% first.
[mintowc,locmintowc] = min([navConfig(:).TOWC]);
mintowc = ceil((mintowc-1)/4)*4 + 1;
[navConfig(:).TOWC] = deal(mintowc);

sc.SampleTime = stepTime;
sc.StartTime = HelperGNSSConvertTime(navConfig(locmintowc).WeekNumber,mintowc*12,gnssStart);
sc.StopTime = sc.StartTime + seconds(simulationDuration);

% Add the extracted satellites to the scenario.
[sc,sat] = HelperAddSatellite(sc,navicData);

Set up a receiver in the scenario. Calculate the Doppler shift and latency for all of the satellites.

rx = groundStation(sc,rxlat,rxlon,Altitude=rxalt,MinElevationAngle=20); % Set up the receiver
ac = access(sat,rx);
acstats = accessStatus(ac);

% This example runs for a maximum of 2 minutes of data and access does not
% change for that duration. Thus, consider, the access status of only the
% first sample time.
satindices = find(acstats(:,1));
numSat = length(satindices);

if ~numSat
    error("No NavIC satellites are detected at the receiver's location. " + ...
        "Ensure that receiver is positioned within the NavIC operational " + ...
        "region and that the RINEX file includes visible satellites for this location.")
end

% Calculate the Doppler shift over time for all the visible satellites.
fShift = dopplershift(sat(satindices),rx,Frequency=fc);

% Calculate signal delays over time for all the visible satellites.
delays = latency(sat(satindices),rx);

PRNIDs = [navConfig(:).PRNID];
disp("Available satellites - " + num2str(PRNIDs(satindices)))
Available satellites - 2   6   9  10

Initialize the waveform writer object.

if WriteWaveformToFile == 1
    bbWriter = comm.BasebandFileWriter("NavICBBWaveform.bb",fs,0);
end

Initialize Waveform Generation Parameters

Specify the parameters required to generate a NavIC waveform. These parameters include signal properties specific to the NavIC standard.

% Set amplitude factor values for interplex modulation.
A1 = 2/3;
A2 = sqrt(2)/3;
A3 = sqrt(2)/3;

oneCACodeDuration = 1e-3;                          % Duration of one C/A-code block, which is equal to the stepTime of simulation
chipRate = 1.023e6;
oneBitduration = 20e-3;                            % Transmission rate for NavIC L5/S is 50bps, Thus, the time taken to
                                                   % transmit one bit is 20 ms.
frameDuration = 48;
numChipsPerCodeBlock = chipRate*oneCACodeDuration;
numCACodeBlocksPerBit = 20;

% Property for counting the received samples from each satellite.
sampleCntr = zeros(1,numSat);
rxistep = 1;                                       % receiver step counter

Initialize the spreading codes and System objects for the receiver chain.

% Find the spreading codes that correspond to the available satellites.
caCode = gnssCACode(PRNIDs(satindices),WaveformType);

% Initialize the signal acquisition and signal tracking objects.
sigAcquisition = gnssSignalAcquirer;
sigAcquisition.SampleRate = fs;
if strcmp(WaveformType,"NavIC L5")
    sigAcquisition.GNSSSignalType = "NavIC L5 C/A";
else
    sigAcquisition.GNSSSignalType = "NavIC S C/A"; %#ok<UNRCH>
end
sigTracker = gnssSignalTracker(GNSSSignalType=sigAcquisition.GNSSSignalType, ...
    SampleRate=fs,PLLNoiseBandwidth=PLLNoiseBW,FLLNoiseBandwidth=FLLNoiseBW,DLLNoiseBandwidth=DLLNoiseBW);

% Properties for storing the outputs of the tracking module.
trackedWave = zeros(numSteps - rxWaitTime,numSat);
trackInfo = struct("PhaseError",[],"PhaseEstimate",[],"FrequencyError",[],"FrequencyEstimate",[], ...
                "DelayError",[],"DelayEstimate",[]);

% Property for storing the outputs of bit synchronization.
bitlocation = zeros(maxNumTrackingChannels,1);

% Initialize the propagation channel object with the waveform sample rate.
gnssChannel = HelperGNSSChannel(RandomStream="mt19937ar with seed",SampleRate=fs);

% Set the random stream seed to control random bits generation.
rngObj = RandStream("mt19937ar","Seed",73);

% Set the first state of the receiver chain, which is the acquisition of the satellites
nxtState = "acquisition";

Generate Navigation Data Bits

Generate the NavIC navigation data bits from the RINEX data extracted using the HelperNavICRINEX2Config helper function. The encoder functions adhere to the NavIC standard and manage all encoding tasks, such as data interleaving and convolutional encoding.

The navigation data bits variable navData contains data bits for all subframes specified in the configuration object. The number of generated data frames is equal to the length of the subframe3MsgID or subframe4MsgID property of the navConfig configuration object. The encoder generates same number of frames for each PRN ID in the configuration object. You can increase or decrease the number of generated frames by adding or removing a valid message type ID from both the subframe3MsgID and subframe4MsgID properties of the navConfig configuration object.

navData = HelperNavICDataEncode(navConfig);

Transmit-Receive Loop

Generate a NavIC waveform for each millisecond of data. The transmitted signal consists of both the NavIC services:

  • Standard positioning services (SPS) — Available to the public for navigational purposes.

  • Restricted service (RS) — An authorized encrypted service, which is not public. The example generates these RS signals as random bits.

To form the final transmit signal, interplex the three signals (SPS-data, RS-data, and RS-pilot) as defined in the NavIC standard [1]. The system resamples the generated signal according to the specified sample rate fs, and then passes it through the propagation channel. The propagation channel impairs the signal with Doppler shift, signal delay, and noise, where the delay and Doppler shift values are derived from the satelliteScenario simulation.

Transmit_block.png

The last part of this processing loop implements a NavIC receiver. The receiver starts with signal acquisition after a number of steps, defined by rxWaitTime, have elapsed, ensuring that the receiver captures meaningful signal rather than pure noise due to the signal delays. These are the steps in the receiver chain.

  • Acquisition — Finds the visible satellites and provides the coarse Doppler frequency offset and timing offset.

  • Tracking — The acquisition module finds the coarse estimates and uses them to initialize the tracking loops. The tracking module in the GNSS receiver uses a phase-locked loop (PLL), frequency-locked loop (FLL), and delay-locked loop (DLL) to ensure precise positioning. The PLL maintains phase alignment, the FLL tracks frequency changes, and the DLL measures signal delay to calculate the distance.

  • Bit-synchronization — Once you have tracked enough samples, defined by the value of numWaitingStepsForBitSync, the gnssBitSynchronize function synchronizes the samples to find the start of a complete data bit. A NavIC data bit consists of 20 C/A-code blocks. Thus, you synchronize the bits by finding the index of maximum transition among a set of 20 C/A-code blocks.

  • Buffer — Indicates a storage state in the receiver chain. The buffer collects the tracked samples of all of the visible satellites until the end of the simulation.

This table provides gives details on the state-based behavior of the receiver. It lists all the five state-based behaviors of a GNSS receiver.

State

Action

Acquisition

Start state of the receiver. Indicates the acquisition module of the receiver.

Tracking

GNSS signal tracking module of the receiver.

Bit-sync

Bit synchronization module.

Data-decode

Frame synchronization and received data decoding module.

Pos-estimate

Position estimation module.

This diagram further illustrates each state-based behavior. The acquisition state is the initial state for the first waveform chunk of stepTime (1 ms) duration. After processing one chunk, the receiver sets a new state for the next waveform chunk and breaks out of the state machine. The next transmitted waveform chunk enters the receiver state machine with the previous state for further processing. This process continues until the entire waveform has been processed.

Receiver_block.png

tic
for istep = 1:numSteps
    % Choose the bit index according to the specified start index and step
    % count.
    bitidx  = floor((istep-1)/numCACodeBlocksPerBit) + 1;
    SPSBits = xor(caCode,navData(bitidx,satindices));
    
    % Modulate the RS signal
    dummyRSP = repelem(repmat(randi(rngObj,[0,1],numChipsPerCodeBlock,1),numSat,1),2,1); % Random binary bits as data for RS-pilot  
    dummyRSD = repelem(repmat(randi(rngObj,[0,1],numChipsPerCodeBlock,1),numSat,1),2,1); % Random binary bits as data for RS-data

    % Modulated SPS
    SPSmod = 1-2*double(SPSBits(:));

    % Rate-match the SPS signal with BOC modulated RS signals
    s2 = repelem(SPSmod,20,1);
    s3 = bocmod(dummyRSP,5,2);
    s1 = bocmod(dummyRSD,5,2);
    [bbwave_temp,eta] = interplexmod([s1,s2,s3],[A1 A2 A3]);
    bbwave = reshape(sqrt(2)*bbwave_temp,length(bbwave_temp)/numSat,numSat);
    
    % Resample according to the specified sample rate
    [upfac,downfac] = rat(fs/20.46e6);
    idx = 1:downfac:upfac*size(bbwave,1);
    iwave = bbwave(ceil(idx/upfac),:);

    % Introduce propagation channel effects to the transmitted signal
    gnssChannel.SignalToNoiseRatio = -10;
    gnssChannel.SignalDelay = delays(:,istep)';
    gnssChannel.FrequencyOffset = fShift(:,istep)';

    iTxWave = gnssChannel(iwave);

    % Optionally write the waveform to a file
    if WriteWaveformToFile == 1
        bbWriter(iTxWave)
    end

    if strcmp(nxtState,"exit")
        break;
    end

    % Receiver
    if istep > rxWaitTime
        while true            
            switch(nxtState)
                case "acquisition"
                    % Initial synchronization
                    [acqd,corrval] = sigAcquisition(iTxWave,1:14); 
                    acqIdx = acqd.IsDetected == 1;
                    detectedSatTable = sortrows(acqd(acqIdx,:));
                    PRNIDsToSearch = detectedSatTable.PRNID;
                    numRxSat = length(PRNIDsToSearch);
                    disp("The detected satellite PRN IDs: " + num2str(PRNIDsToSearch'))

                    if sum(acqIdx) > 3 
                        % If three or more satellites are detected
                        sigTracker.InitialFrequencyOffset = detectedSatTable.FrequencyOffset;
                        sigTracker.InitialCodePhaseOffset = detectedSatTable.CodePhaseOffset;
                        sigTracker.PRNID = PRNIDsToSearch;
                        nxtState = "tracking";

                        if ShowVisualizations
                            % Correlation plot for first detected satellite
                            figure
                            mesh(-10e3:500:10e3,0:size(corrval,1)-1, corrval(:,:,PRNIDsToSearch(1)))
                            xlabel("Doppler Offset")
                            ylabel("Code Phase Offset")
                            zlabel("Correlation")
                            msg = ["Correlation Plot for PRN ID: " num2str(PRNIDsToSearch(1))];
                            title(msg)
                        end
                    else
                        % If acquisition fails, that is, less than three
                        % satellites are detected, the receiver state
                        % remains the same, which is acquisition.
                        nxtState = "exit";
                        break
                    end

                case "tracking"
                    [trackedWave(rxistep,:),trackInfo(rxistep)] = sigTracker(iTxWave);
                    if any(bitlocation)
                        % Once bit synchronization is complete, store the
                        % samples.
                        nxtState = "buffer";
                    elseif rxistep > numWaitingStepsForBitSync
                        % If enough samples are collected, move to bit
                        % synchronization state.
                        nxtState = "bit-sync";
                    else
                        % Exit the receiver machine. State remains
                        % "tracking" for next sample.
                        break
                    end
                case "bit-sync"
                    for isat = 1:numSat
                        [bitlocation(isat),numtr] = gnssBitSynchronize( ...
                            imag(trackedWave(1:numWaitingStepsForBitSync,isat)), ...
                            numCACodeBlocksPerBit);
                        rxSamples{isat} = trackedWave(bitlocation(isat):end,isat);
                        sampleCntr(isat) = rxistep - bitlocation(isat) + 1;
                    end
                    nxtState = "tracking";
                    break;
                case "buffer"
                    % Once bit synchronization is successful, the receiver
                    % machine switches between "tracking" and "buffer"
                    % states for each step
                    for isat = 1:numSat
                        sampleCntr(isat) = sampleCntr(isat) + 1; 
                        rxSamples{isat}(sampleCntr(isat)) = trackedWave(rxistep,isat);
                    end
                    nxtState = "tracking";
                    break
            end
        end
    rxistep = rxistep + 1;
    end
    if ~mod(istep,1000)
        disp("Processed " + (istep/1000) + " sec of data at the receiver.")
    end
end
The detected satellite PRN IDs: 2   6   9  10

Figure contains an axes object. The axes object with title Correlation Plot for PRN ID: 2, xlabel Doppler Offset, ylabel Code Phase Offset contains an object of type surface.

Processed 1 sec of data at the receiver.
Processed 2 sec of data at the receiver.
Processed 3 sec of data at the receiver.
toc
Elapsed time is 68.444909 seconds.
rxistep = rxistep - 1;
if WriteWaveformToFile
    % Release the waveform writer object
    release(bbWriter)
end
if ShowVisualizations
    % Show the last 1000 received samples
    rxconstellation = comm.ConstellationDiagram(1,ShowReferenceConstellation=false, ...
        Title="Constellation diagram of signal at the output of tracking");
    rxconstellation(trackedWave(max(1,rxistep-999):rxistep,1)/rms(trackedWave(max(1,rxistep-999):rxistep,1)))
end

Decode Received Data

Synchronize the frames and decode the data of the received symbols. Frame synchronization is successful when the parts of the received samples match sync-code defined in the NavIC standard. The synchronization function provides the start index of a subframe in the received symbols, which is then used for data decoding.

Initialize Parameters

Initialize the parameters to for configuring the frame synchronization object and storing the decoded values.

syncCode = [1 1 1 0 1 0 1 1 1 0 0 1 0 0 0 0]';   % Synchronization code (sync-code) as defined in the NavIC standard
numSymPerSF = 600;
numStepPerSym = 20;
numStepsPerSF = numSymPerSF*numStepPerSym; 
maxDetectLen = 3*numSymPerSF;                    % Maximum data length to compare sync-code
rxNavConfig = struct();                          % Decoded data structure
satSFstartIdx = zeros(numRxSat,1);               % To store the frame synchronization index

Perform Frame Synchronization and Data Decoding

The rxSamples carries the received samples in the form of repeated C/A-code blocks, that is, 20 C/A-code blocks are repeated with the value of each data bit. To calculate an average symbol, average the received samples over the number of code blocks per bit numCACodeBlocksPerBit.

The frame synchronization algorithm compares the computed symbols with the sync-code, looking for a hard-correlation of 100%. As you know that a NavIC subframe is 600 symbols long, to ensure that the synchronization is valid, the algorithm also computes the synchronization index between two subsequent subframes. The first synchronization index becomes the start index of the received symbols, and the HelperNavICDataDecode decodes the symbols if the frame synchronization is valid.

for isat = 1:numRxSat
    if sampleCntr(isat) >= 4*numStepsPerSF
        avgSym = mean(reshape(rxSamples{isat}(1:end-mod(sampleCntr(isat),numCACodeBlocksPerBit)), ...
            numCACodeBlocksPerBit,[]));
        % Obtain symbols from the received IQ samples.
        rxSyms = imag(avgSym.') < 0;
        % Synchronize the frame, and obtain the subframe start index
        [rxSyms,SFstartIdx] = HelperGNSSFrameSynchronize(rxSyms,WaveformType);
        correctLen = ceil(length(rxSyms)/numSymPerSF)*numSymPerSF;
        rxSyms = [rxSyms; zeros(correctLen - length(rxSyms),1)];
        % Decode the symbols
        if SFstartIdx
            [rxNavConfig,crcError] = HelperNavICDataDecode(rxSyms,rxNavConfig);
            satSFstartIdx(isat) = SFstartIdx;
        end
    end
end
% Move to position estimation state if frame synchronization is successful
% for at least 4 satellites
if length(satSFstartIdx) > 3
    if sampleCntr(isat) >= 4*numStepsPerSF
        nxtState = "pos-estimate";
    end
end

NavIC Receiver Position Estimation

Once you have successfully decoded, the next and final state of the receiver chain is position estimation. Receiver position estimation requires a minimum of four satellites. You can divide the receiver position estimation process into these steps.

  • Pseudorange Calculation — Calculate the pseudorange to each selected satellite. This involves measuring the time it takes for a satellite signal to reach the receiver and converting this time into a distance. However, you cannot calculate the travel time precisely due to factors such as atmospheric effects and satellite-receiver clock discrepancies. Thus, you must estimate the time using the ranging codes.

  • Satellite Position Estimation — Determine the precise position of the selected satellites at the time of signal transmission. The satellite ephemeris provides the information about the satellite orbit and can be used to calculate its position in space.

  • Receiver Position Estimation — Estimate the receiver position using the computed pseudoranges and their corresponding satellite positions.

Pseudorange Computation

Estimate the pseudoranges of the detected satellites.

rho = zeros(numSat,1);
if nxtState == "pos-estimate"
    codeOffsetTime = sigTracker.InitialCodePhaseOffset(1:numSat)/chipRate;
    trackingOffsetTime = trackInfo(rxistep).DelayEstimate/chipRate;
    bitsyncTime = (bitlocation(1:numSat) - 1)*oneCACodeDuration;
    framesyncTime = (satSFstartIdx-1)*numCACodeBlocksPerBit*oneCACodeDuration;
    % Calculate transmission time from these parameters
    delayEst = (codeOffsetTime + bitsyncTime + framesyncTime - trackingOffsetTime');
    rho = delayEst*c;
end

Satellite Position Estimation

Estimate the satellite position at the time of signal transmission.

if simulationDuration < 60
    % Receiver position estimation requires a minimum  60 seconds
    % simulationDuration. This code block is executed when
    % simulationDuration < 60 s, that is, sufficient data has not been
    % transmitted. It loads a MAT file with decoded data and pseudoranges
    % for a 60 s simulation and estimates the receiver position.
    load NavICreceiverPositionProperties;

    defaultRINEXFileName = "IITK00IND_R_20243400400_01H_MN.rnx";
    defaultRxPos = [28.6329, 77.2195, 100];
    exampleRxPos = [rxlat, rxlon, rxalt];

    if ~(strcmp(rinexFileName,defaultRINEXFileName) && isequal(defaultRxPos,exampleRxPos))
        warning("satcom:EndToEndNavICConstellationSimulation:InsufficientData", ...
            "Estimated receiver position may be different from what you provided" + ...
            " as the simulation didn't run for entire data." + ...
            " To get accurate receiver position, run the example" + ...
            " for at least 60 seconds of navigation data.")
    end
    [rxlat,rxlon,rxalt] = deal(defaultRxPos(1),defaultRxPos(2),defaultRxPos(3));
    nxtState = "pos-estimate";
end

if nxtState == "pos-estimate"
    eph = rxNavConfig.Ephemeris;
    % Add constant to account for NavIC week number rollover
    IRNWeek = rxNavConfig.TOWC.WN + 1024;
    % Account for the time of week count (TOWC). Add one, because
    % pseudorange is computed at the end of the last received subframe.
    towc = rxNavConfig.TOWC.TOWC + 1;

    clock = rxNavConfig.Clock;
    refTime = HelperGNSSConvertTime(IRNWeek,clock.t_oc,gnssStart);
    % Construct time-table of the received clock and ephemeris data.
    rxtimetable = timetable(refTime,eph.PRNID,clock.a_f0,clock.a_f1,clock.a_f2,eph.IODEC, ...
        eph.C_rs,eph.delta_n*pi,eph.M_o*pi,eph.C_uc,eph.e,eph.C_us,eph.sqrtA,eph.t_oe,eph.C_ic, ...
        eph.omega_o*pi,eph.C_is,eph.i_0*pi,eph.C_rc,eph.AOP*pi,eph.RateOfRAAN*pi,eph.IDOT*pi,IRNWeek,clock.T_GD);
    rxtimetable.Properties.VariableNames = [navicData.Properties.VariableNames(1:21) {'IRNWeek'} {'TGD'}];   

    % Compute the space vehicle bias correction.
    tk = towc*12 - rxtimetable.Toe;
    svBiasCorrection = -seconds(rxtimetable.SVClockBias + rxtimetable.SVClockDrift.*tk);
    dts = svBiasCorrection;
    % Compute the satellite transmission time.
    transmissionTime = HelperGNSSConvertTime(IRNWeek,towc*12,gnssStart)+svBiasCorrection;

    % Determine the satellite positions at the corrected transmission
    % times.
    satpos = zeros(numSat,3);
    for isat = 1:numSat
        [satpos(isat,:),~] = gnssconstellation(transmissionTime(isat),rxtimetable(isat,:));
    end
end

Receiver Position Estimation

Estimate the receiver position, and find the distance error from the actual position.

if nxtState == "pos-estimate"
    [rxposest,~,hdop,vdop] = receiverposition(rho,satpos);
    estRxPosNED = lla2ned(rxposest,[rxlat rxlon rxalt],"ellipsoid");
    distanceError = vecnorm(estRxPosNED);                            % In meters
    disp("Position estimation error is " + distanceError + " meters")
    if hdop > 20
        warning("Dilution of Precision (DOP) ratings are poor. The position " + ...
            "estimation error can be high.")
    end
end
Position estimation error is 29.5468 meters

Further Exploration

The example uses a RINEX file with four visible satellites. You can try using a different RINEX file, or a RINEX file with a larger number of visible satellites.

Supporting Files

References

[1] Indian Space Research Organization (ISRO). Indian Regional Navigation Satellite System Signal in Space ICD for Standard Positioning Service. ISRO-IRNSS-ICD-SPS-1.1. ISRO, Bangalore: August 2017.

See Also

Functions

Objects

Topics