Matlab Generate Gnss Waveform
MathWorksOfficialCreated by MathWorks
- Skill
- Workflow
- Science and research
- BSD-2-Clause
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured workflow for generating GNSS baseband waveforms using the Satellite Communications Toolbox. It supports GPS, Galileo, and NavIC constellations, allowing users to simulate signals either through physically realistic satellite scenarios based on orbital dynamics or by providing custom channel parameters like Doppler, delay, and SNR. The workflow ensures that navigation data is correctly encoded, enabling end-to-end receiver testing for position estimation.
What it does
- Generates baseband waveforms for GPS, Galileo, and NavIC signals.
- Computes realistic Doppler, delay, and SNR using satellite scenarios.
- Supports custom channel impairment values for controlled testing.
- Encodes navigation data using almanac or RINEX files for position estimation.
- Configures waveform generators for specific signal types and constellations.
- Provides a systematic pipeline for multi-satellite signal simulation.
When to use it
- When you need to generate GNSS signals for receiver algorithm testing.
- When simulating multi-satellite channels with realistic orbital dynamics.
- When performing controlled testing with specific Doppler, delay, or SNR values.
- When building end-to-end simulations that require decodable navigation data.
Advantages
- Enables realistic simulation of GNSS signal propagation.
- Supports both scenario-driven and custom channel modeling.
- Facilitates end-to-end receiver testing with valid ephemeris data.
- Provides a consistent workflow across multiple GNSS constellations.
Usage examples
- “Generate a GPS L1 C/A waveform for a receiver in London using a satellite scenario.”
- The agent will configure a satellite scenario, compute Doppler and delay based on the location, encode LNAV data, and generate the baseband waveform.
- “Create a custom Galileo E1 signal with a static Doppler shift of 500 Hz and 10 dB SNR.”
- The agent will set up the Galileo waveform generator, apply the user-specified custom channel parameters, and output the baseband signal.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/matlab/matlab-generate-gnss-waveform/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip matlab-generate-gnss-waveform.zip -d .agents/skills
Documentation by MathWorks
Generate GNSS Waveform
Generate multi-satellite GNSS baseband waveforms with properly encoded navigation data. Channel impairments (Doppler, delay, noise) can come from a realistic satellite scenario OR from user-specified custom values. Supports GPS, Galileo, and NavIC constellations. The waveform carries real ephemeris so a receiver can decode it for position estimation.
When to Use
Generating GNSS baseband signals (GPS, Galileo, or NavIC) for receiver testing
Simulating multi-satellite channels with Doppler and delay from orbital dynamics
Testing with specific Doppler/delay/SNR values (custom channel mode)
Building end-to-end GNSS receiver simulations that need decodable nav data
When NOT to Use
Signal acquisition, tracking loops, nav decoding, or position estimation
BeiDou/GLONASS (not supported) or SDR hardware transmission
Prerequisites: Helper Files
Copy helper files from MathWorks examples into your project folder.
See references/helper-files.md for the full list of source examples
and files to copy per constellation (GPS, Galileo, NavIC).
Workflow
The pipeline has 6 steps (0-5). Step 0 determines signal type, channel mode, and data source. The channel mode decides whether Steps 2-3 are needed.
Step 0: Determine signal type + channel mode + data source + nav data intent
Step 1: Configure waveform generator
├── Scenario mode → Step 2 (satellite scenario) + Step 3 (compute channel from physics)
└── Custom mode → Step 3c (user provides Doppler/delay/SNR directly)
Step 4: Encode navigation data (both modes)
Step 5: Generate waveform with channel (both modes)Step 0: Determine signal type, channel mode, and data source
If the user's prompt does not clearly specify the signal type, ask:
What GNSS waveform would you like to generate?
GPS: L1 C/A, L1C, L2C, L5 Galileo: E1, E1C (pilot only), E5a, E5b, E5 (wideband) NavIC: L5, S, L1
Also, how should channel impairments (Doppler shift, propagation delay, noise) be applied?
(a) Scenario-driven — I compute realistic values from a satellite scenario. You provide a receiver location (lat/lon) and I handle the orbital dynamics.
(b) Custom values — You provide Doppler (Hz), delay (s), and/or SNR (dB) per satellite. Useful for controlled testing or when you already have your own channel model.
Detection heuristics: Location/city/lat-lon → Scenario. Specific Doppler/delay/SNR numbers → Custom. "Stress test" or "extreme" → Custom. No cues → Ask. If signal type is clear but mode ambiguous, ask only about mode.
Data source selection
Available: gpsAlmanac.txt (always on path), RINEX .rnx files
(Nav Toolbox). Require setup: galileoAlmanac.xml (copy from
example), fresh almanac (download from USCG NAVCEN).
Decision rules:
User provides RINEX → use for both scenario AND nav data.
No RINEX → use bundled almanac (
gpsAlmanac.txtorgalileoAlmanac.xml).Current-epoch needed → offer fresh almanac download (requires internet).
RINEX requested but no Nav Toolbox → fall back to almanac or parse manually (see
references/rinex-integration.md).
All bundled files are from a fixed past epoch. This is fine for receiver algorithm testing (orbital geometry is still valid) but does not represent today's actual sky. The agent MUST communicate which file and epoch is being used.
Navigation data intent
Navigation data can be:
Real ephemeris (from almanac/RINEX) — required for end-to-end receiver testing where position estimation must work
Random/dummy bits — valid for signal-level testing (acquisition, tracking, modulation quality) where nav decode is not the goal
If user's intent is unclear, ask:
Do you need the waveform to carry real navigation data (for position estimation), or are dummy bits fine (for signal-level testing)?
STOP — Present choices before generating
Before writing or executing ANY code, print a standalone configuration summary stating: signal type, center frequency, sample rate, duration, data source + epoch, start time, and nav data type. This MUST appear as visible text BEFORE any code block — not buried in comments or post-run summaries. In interactive mode, wait for user confirmation.
Step 1: Configure waveform generator (constellation-specific)
GPS:
sampleRate = 5e6;
wavegenobj = gpsWaveformGenerator(SampleRate=sampleRate);
wavegenobj.SignalType = "legacy"; % L1 C/A
navDataType = "LNAV";
centerFrequency = 1575.42e6;
stepTime = wavegenobj.BitDuration;Verify: stepTime should be 0.02 (20 ms) for L1 C/A, 0.01 for L1C/L5.
Galileo:
sampleRate = 25e6; % Use 60e6 for E5 wideband
wavegenobj = galileoWaveformGenerator(SampleRate=sampleRate);
wavegenobj.SignalType = "E1"; % or "E1C","E5a","E5b","E5"
centerFrequency = 1575.42e6; % E5 uses 1191.795e6
stepTime = wavegenobj.BitDuration;Verify: stepTime should be 0.004 (4 ms) for E1/E1C/E5b, 0.02 for E5a/E5.
E5 has critical pitfalls — nav data must be a {fnavbits, inavbits}
cell array (not a matrix), and I/NAV needs 5× more rows than F/NAV.
See references/galileo-pipeline.md E5 section.
NavIC (R2026a -- no system object yet):
sampleRate = 24e6;
centerFrequency = 1176.45e6; % L5 (or 2492.028e6 for S, 1575.42e6 for L1)
stepTime = 0.02; % 20 ms for L5/S, 10 ms for L1NavIC L1 is completely different from L5/S (SBOC, IZ4 codes, support file download). See references/navic-pipeline.md §NavIC L1 SPS.
See references/gps-pipeline.md, references/galileo-pipeline.md, or references/navic-pipeline.md for full signal type tables.
Step 2: Set up satellite scenario (SCENARIO MODE ONLY)
Skip this step entirely in custom mode.
Create the scenario, ground station, and timing (shared). Then add satellites using the constellation-specific method.
sc = satelliteScenario;
rx = groundStation(sc, lat, lon, Altitude=alt);
rx.MinElevationAngle = 10;
waveDuration = 10; % seconds
sc.StartTime = startTime;
sc.StopTime = sc.StartTime + seconds(waveDuration - stepTime);
sc.SampleTime = stepTime;Add satellites (constellation-specific). Default to almanac unless the
user explicitly has a RINEX file or asks for RINEX. The almanac path avoids
a Navigation Toolbox dependency (rinexread). The same source must feed
Step 4 (nav data encoding).
GPS (almanac path — preferred):
sat = satellite(sc, "gpsAlmanac.txt", OrbitPropagator="gps");GPS (RINEX path): Check ~isempty(ver('nav')) first. If Navigation
Toolbox is unavailable, use almanac or parse RINEX manually
(see references/rinex-integration.md).
rinexdata = rinexread(rinexFileName);
sat = satellite(sc, rinexdata, OrbitPropagator="gps");Galileo (almanac path — preferred) -- satellite() does NOT accept
Galileo XML almanac files. Use the helper:
sat = HelperAddGalileoSatellitesToScenario(sc, "galileoAlmanac.xml");Galileo (RINEX path): Same toolbox check; fall back to almanac if unavailable.
rinexdata = rinexread(rinexFileName);
sat = satellite(sc, rinexdata);NavIC -- RINEX only (requires Navigation Toolbox).
Use HelperAddSatellite(sc, selectedRows) — see references/navic-pipeline.md.
Critical: Use the same startTime and data source (almanac OR RINEX)
for both orbit propagation (this step) and nav data encoding (Step 4).
Verify: After adding satellites, check sat is non-empty. For GPS
almanac, expect ~30 satellites total in the scenario.
Step 3: Compute channel parameters from physics (SCENARIO MODE ONLY)
Skip this step entirely in custom mode — go to Step 3c instead.
dopShifts = dopplershift(sat, rx, Frequency=centerFrequency).';
ltncy = latency(sat, rx).';Compute SNR from free-space path loss:
DtLin = db2pow(12); % Transmit antenna directivity
DrLin = db2pow(4); % Receive antenna directivity
k = physconst("boltzmann");
T = 300; % Temperature (K)
Pr = Pt*DtLin*DrLin ./ ((4*pi*(centerFrequency + dopShifts).*ltncy).^2);
snrs = 10*log10(Pr/(k*T*sampleRate)) + 3;| Constellation | Pt (Transmit Power) |
|---|---|
| GPS | 44.8 W |
| Galileo | 160 W |
| NavIC | 50 W |
Find visible satellites (non-NaN latency at first time step):
satIndices = find(~isnan(ltncy(1,:)));Verify: numel(satIndices) should be >= 4 (minimum for positioning).
If zero, the start time does not match the data source epoch (Gotcha #12).
Step 3c: Set up custom channel parameters (CUSTOM MODE ONLY)
Skip this step entirely in scenario mode.
The user provides Doppler, delay, and/or SNR values directly.
numSats = 4;
dopShifts = [2000, -1500, 800, 3200]; % Hz per satellite
ltncy = [0.072, 0.074, 0.069, 0.078]; % seconds per satellite
snrs = [-10, -12, -11, -9]; % dB per satellite
% For time-varying custom values, use matrices (numsteps x numSats)
% dopShifts = [...]; % each row is one time step
waveDuration = 10; % seconds
numsteps = round(waveDuration / stepTime);
satIndices = 1:numSats;Required from user: number of satellites, Doppler (Hz), delay (s), PRN/SVID list. Optional (defaults): SNR (-10 dB), duration (10 s), time-varying (no, static).
Expand static values for the generation loop:
if isvector(dopShifts) && size(dopShifts,1) == 1
dopShifts = repmat(dopShifts, numsteps, 1);
ltncy = repmat(ltncy, numsteps, 1);
snrs = repmat(snrs, numsteps, 1);
endNo physical constraint validation in custom mode. The user may intentionally provide extreme values. Do NOT assert against physical ranges.
Step 4: Encode navigation data (BOTH MODES, constellation-specific)
Navigation data carries ephemeris the receiver needs for position estimation.
If user needs real ephemeris (position estimation):
Use the same data source as the scenario (Step 2)
If RINEX was used for the scenario, use RINEX for nav data too (preferred)
Almanac supplements non-ephemeris fields only (health bits, almanac pages)
Start time for nav data encoding MUST match scenario start time — mismatched times produce nav bits that don't correspond to satellite positions, causing silent position estimation errors
If user only needs signal-level testing (acquisition/tracking):
Random bits are acceptable:
navdata = randi([0 1], numsteps, numSats);No almanac or RINEX files needed at all
Waveform will have correct modulation and channel but nav decode will fail
Critical: When RINEX was used for orbit propagation in Step 2, you MUST
use HelperGPSRINEX2Config (not HelperGPSAlmanac2Config) for nav data
encoding. The same RINEX struct feeds both orbit and ephemeris. Falling back
to almanac for nav data while using RINEX for the scenario violates the
single-source principle and may produce inconsistent ephemeris.
Scenario mode with real ephemeris: satIndices comes from visible
satellite computation in Step 3.
Custom mode with real ephemeris: Use almanac as the data source (no Navigation Toolbox needed). The user must specify which PRN/SVID values to encode. Use the almanac path examples below.
GPS (almanac path):
navcfg = HelperGPSAlmanac2Config(almFileName, navDataType, satIndices, startTime);
visiblePRN = [navcfg(:).PRNID];
% Encode nav data per satellite (HelperGPSNAVDataEncode accepts scalar config only)
tempnavdata = HelperGPSNAVDataEncode(navcfg(1));
navdata = zeros(length(tempnavdata), length(navcfg));
navdata(:,1) = tempnavdata;
for isat = 2:length(navcfg)
navdata(:,isat) = HelperGPSNAVDataEncode(navcfg(isat));
endGPS (RINEX path) -- see references/gps-pipeline.md for the full
HelperGPSRINEX2Config pattern.
Galileo (almanac path): Use the nav data signal type mapping from
references/galileo-pipeline.md (E1→E1B, E5a→E5a, E5b→E5b):
galNavObj = HelperGalileoNavigationData(almFileName, true, SignalType="E1B");
visibleSVIDs = galNavObj.Ephemeris.SatelliteID(satIndices);
[~, inittow] = HelperGPSConvertTime(startTime);
navdata = navdata2bits(galNavObj, visibleSVIDs, inittow, numpages);Galileo (RINEX path) -- see references/galileo-pipeline.md.
NavIC -- RINEX only. HelperNavICRINEX2Config takes the RINEX data
table directly (not a filename):
navicCfg = HelperNavICRINEX2Config(selectedRows);See the constellation-specific reference files for complete encoding patterns.
Step 5: Generate waveform with channel (BOTH MODES)
This step is identical for both scenario and custom modes. By Step 5,
dopShifts, ltncy, and snrs are all numsteps x numSats matrices
regardless of how they were obtained.
GPS and Galileo use their system object in a stepped loop:
wavegenobj.PRNID = visiblePRN; % GPS uses PRNID
% wavegenobj.SVID = visibleSVID; % Galileo uses SVID
gnsschannelobj = HelperGNSSChannel( ...
FrequencyOffset=dopShifts(1,satIndices), ...
SignalDelay=ltncy(1,satIndices), ...
SignalToNoiseRatio=snrs(1,satIndices), ...
SampleRate=sampleRate, ...
RandomStream="mt19937ar with seed", Seed=73);
numsteps = round(waveDuration / stepTime);
samplesPerStep = sampleRate * stepTime;
waveform = zeros(numsteps * samplesPerStep, 1);
for istep = 1:numsteps
idx = (istep-1)*samplesPerStep + (1:samplesPerStep);
waveform(idx,:) = gnsschannelobj(wavegenobj(navdata(istep,:)));
gnsschannelobj.SignalToNoiseRatio = snrs(istep, satIndices);
gnsschannelobj.FrequencyOffset = dopShifts(istep, satIndices);
gnsschannelobj.SignalDelay = ltncy(istep, satIndices);
endNavIC (R2026a) uses HelperNavICBBWaveform or manual modulation
(no system object). The helper returns multi-column output (one per
satellite) -- pass directly to HelperGNSSChannel, do not sum first.
See references/navic-pipeline.md for the full pattern.
Verify: length(waveform) == numsteps * samplesPerStep. Waveform
should not be all-zero (would indicate channel SNR too low or nav data
mismatch).
Signal Type Quick Reference
See references/signal-type-mapping.md for full tables of SignalType
strings, center frequencies, BitDuration, and nav data types per
constellation.
Verification (Scenario Mode Only)
After generating a waveform in scenario mode, verify channel parameters are
physically realistic. See references/physical-constraints.md for the full
verification function and expected ranges per constellation. Key checks:
at least 4 visible satellites, Doppler within ±10 kHz (L1), delay 67-86 ms
(MEO) or 119-130 ms (GEO/GSO), distinct Doppler per satellite, and
time-varying Doppler.
Do not run these checks in custom mode — the user may intentionally provide values outside physical ranges.
Key Functions
| Function | Purpose | Toolbox |
|---|---|---|
gpsWaveformGenerator | Generate GPS baseband waveform | Satellite Communications |
galileoWaveformGenerator | Generate Galileo baseband waveform | Satellite Communications |
satelliteScenario | Create scenario (scenario mode) | Satellite Communications |
dopplershift | Compute Doppler from orbital dynamics | Satellite Communications |
latency | Compute propagation delay | Satellite Communications |
rinexread | Read RINEX navigation files (RINEX path only) | Navigation (optional) |
physconst | Physical constants (Boltzmann, light speed) | Core MATLAB |
Gotchas
Random nav bits only if user explicitly agrees.
randi([0 1], ...)is acceptable for signal-level testing (acquisition, tracking) but makes position estimation impossible. If user needs end-to-end receiver testing, use the constellation-specific nav data encoder for real ephemeris. Always confirm nav data intent in Step 0.In scenario mode, never hand-roll Doppler or delay. Do not use
exp(1j*2*pi*f*t)for Doppler orcircshiftfor delay. UsesatelliteScenario->dopplershift()->latency()->HelperGNSSChannelfor physics-based channel modeling. In custom mode, user-provided values are passed directly toHelperGNSSChannel.Nav data has more rows than numsteps. Nav data encoders produce the full message (e.g., 37500 rows for GPS LNAV) because they encode the complete nav frame structure. Only the first
numstepsrows are consumed in the generation loop. Do not resize or assert equality — excess rows are simply unused.RINEX: pass struct, not filename.
satellite(sc, rinexData)expects the struct fromrinexread(), not the.rnxfilename string. Passing the filename causes a confusing "invalid orbit data" error.Center frequency must match signal type. L1/E1 = 1575.42 MHz, L2 = 1227.60 MHz, L5/E5a = 1176.45 MHz, E5b = 1207.14 MHz, E5 = 1191.795 MHz, S = 2492.028 MHz. Must match in
dopplershift().GPS uses
PRNID, Galileo usesSVID. Using the wrong property name silently creates a dynamic property on the system object — the waveform generator ignores it and generates with default satellite IDs, producing incorrect spreading codes.Galileo transmit power is 160 W, not GPS's 44.8 W. Using GPS power for Galileo gives incorrect SNR. NavIC uses 50 W.
NavIC has no system object in R2026a. Calling
navicWaveformGeneratorwill error. Use manual modulation (interplexmod,bocmod,gnssCACode) or theHelperNavICBBWaveformhelper. Ships as a system object in R2026b.satellite()only accepts GPS SEM almanac natively. Passing a Galileo XML or NavIC RINEX file directly tosatellite()errors or produces wrong orbits. Use the constellation-specific helpers in Step 2.GPS L5 and L1C: not all satellites broadcast modernized signals. Block IIF+ carry L5; Block III+ carry L1C. Expect ~4-6 visible vs ~8-12 for L1 C/A.
Filter PRN/SVID against nav data availability. Intersect visible satellites with the nav data object's available IDs before encoding.
Time/epoch consistency is critical. Three times must align:
sc.StartTime, the nav data encoder'sstartTime, and the data source epoch. Ifsc.StartTimeis far from the almanac/RINEX epoch,latency()returns all-NaN (zero visible satellites, no error). If nav datastartTimediffers fromsc.StartTime, ephemeris won't match satellite positions — causing silent position errors. Bundled files are from a fixed past epoch; always communicate which file and epoch is used.
Conventions
datetimewithTimeZone="UTC"for all GNSS time referencesName-value syntax (
Name=Value), not legacy'Name', ValuepairsGPS time conversion:
HelperGPSConvertTime; Galileo/NavIC:HelperGNSSConvertTimeAlways transpose
dopplershift/latencyoutput (.';) — they return column-per-timestep, but the generation loop indexes row-per-timestepPre-allocate the waveform:
zeros(numsteps * samplesPerStep, 1)Use
seconds()for duration arithmetic (not bare numbers)Signal type strings are case-sensitive:
"legacy"not"Legacy","l5"not"L5"Prefer
find(~isnan(ltncy(1,:)))to detect visible satellites — simpler and more robust than elevation angle filtering
Copyright 2026 The MathWorks, Inc.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.1 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md20.0 KiB
- manifest.yaml573 B
- references/galileo-pipeline.md10.4 KiB
- references/gps-pipeline.md5.2 KiB
- references/helper-files.md2.9 KiB
- references/navic-pipeline.md10.8 KiB
- references/physical-constraints.md4.4 KiB
- references/rinex-integration.md5.2 KiB
- references/signal-type-mapping.md2.4 KiB
references/navic-pipeline.md
NavIC Waveform Pipeline
NavIC-specific steps for the GNSS waveform generation workflow. This covers Steps 1, 4, and 5. Steps 2-3 (scenario + channel params) are in SKILL.md.
Important: NavIC does not have a system object in R2026a. Use manual
modulation with interplexmod, bocmod, and gnssCACode. The
navicWaveformGenerator system object ships in R2026b.
Example Setup
openExample('satcom/NavICWaveformGenerationExample')
cd(fileparts(which('HelperNavICBBWaveform')))For the E2E simulation example (additional helpers):
openExample('shared_nav_satcom/EndtoEndNavICConstellationSimulationExample')Helpers provided:
| Helper | Source Example | Purpose |
|---|---|---|
HelperGNSSChannel | E2E | System object: applies Doppler, delay, AWGN per satellite |
HelperNavICBBWaveform | Waveform | Baseband waveform generator (wraps manual modulation) |
HelperNavICConfig | E2E | NavIC satellite configuration |
HelperNavICRINEX2Config | E2E | Converts RINEX data table to NavIC nav config |
HelperNavICDataEncode | Waveform | Encodes NavIC nav data bits |
HelperAddSatellite | E2E | Adds NavIC satellites to scenario from RINEX data |
HelperGNSSConvertTime | E2E | NavIC time conversion |
Step 1: Configure Waveform Parameters
Since there is no system object, configure parameters manually:
sampleRate = 24e6;
centerFrequency = 1176.45e6; % L5 band
stepTime = 0.02; % 20 ms per step
samplesPerStep = sampleRate * stepTime;Signal Mapping
| NavIC Signal | Frequency Band | Center Freq (MHz) | Code/Modulation |
|---|---|---|---|
| L5 SPS | L5 | 1176.45 | gnssCACode("NavIC L5-SPS") + BOC(1,1) |
| S SPS | S-band | 2492.028 | gnssCACode("NavIC S-SPS") + BOC(1,1) |
| L1 SPS | L1 | 1575.42 | IZ4 codes (from .mat file) + SBOC |
NavIC L1 SPS
L1 uses a completely different modulation and code generation approach than L5/S:
Modulation: SBOC (Synthesized BOC), not standard BOC
Spreading codes: IZ4 (Interleaved Z4) linear codes, loaded from
NavICL1SpreadingCodeInitialConditions.matChannel coding: LDPC, parity matrix from
NavICL1LDPCParityCheckMatrixIndices.matData rate: 100 bps
Code rate: 1.023 Mcps
Frame duration: 18 seconds
Required Support Files (Download Before Use)
NavIC L1 SPS requires two .mat support files that are NOT bundled with
the toolbox. They must be downloaded from MathWorks before generating L1
waveforms. Without these files, L1 waveform generation will fail.
Download and unzip using websave:
% Download NavIC L1 support files (one-time setup)
if ~exist('NavICL1SpreadingCodeInitialConditions.mat','file') || ...
~exist('NavICL1LDPCParityCheckMatrixIndices.mat','file')
if ~exist('L1Dataset.zip','file')
url = 'https://ssd.mathworks.com/supportfiles/spc/satcom/NavIC/L1Dataset.zip';
websave('L1Dataset.zip', url);
end
unzip('L1Dataset.zip'); % Extracts .mat files into current folder
endThe zip contains the two .mat files directly (no subdirectories). Unzipping
into the working folder makes them available via load().
Terms of Use: The NavIC L1 PRN codes and LDPC matrices are governed by the NavIC ICD Terms of Use.
L1 Configuration
sampleRate = 24e6;
centerFrequency = 1575.42e6; % L1 band
stepTime = 0.01; % 10 ms per step (NOT 20 ms like L5/S)
% Load L1-specific support files (must already be on path -- see download above)
L1PRNInit = load("NavICL1SpreadingCodeInitialConditions.mat");
load("NavICL1LDPCParityCheckMatrixIndices.mat");Step time is 10 ms for L1 (1 bit per step at 100 bps), compared to 20 ms for L5/S. Nav data produces 5400 rows per satellite (vs 7200 for L5/S).
L1 vs L5/S: Critical Differences
| Aspect | L5/S SPS | L1 SPS |
|---|---|---|
| Step time | 20 ms | 10 ms |
| Nav data rows | 7200 per satellite | 5400 per satellite |
| Spreading codes | gnssCACode("NavIC L5-SPS", prnID) | IZ4 from .mat file |
| Modulation | BOC(1,1) via bocmod | SBOC via sbocmod |
| Helper signature | HelperNavICBBWaveform(navData, "NavIC L5-SPS", [], PRNIDs, fs) | HelperNavICBBWaveform(navData, "NavIC L1-SPS", L1PRNInit, PRNID, fs, bitIdx) |
| PRN argument | Vector of all visible PRN IDs | Single PRN ID (loop per satellite) |
| Extra arguments | None | L1PRNInit struct + bitIdx (1-based step counter) |
Key difference: Do NOT use gnssCACode for L1 — it does not support
"NavIC L1-SPS". Use the IZ4 codes from the .mat file instead.
Step 2: Scenario Setup
NavIC uses RINEX data only (no almanac path). Read RINEX, select one
ephemeris row per satellite, then add to scenario using HelperAddSatellite.
HelperAddSatellite expects the RINEX data table (with SatelliteID
field), NOT config objects.
rinexData = rinexread(rinexFile);
navicRinex = rinexData.NavIC;
navicRinex.Time.TimeZone = "UTC"; % Required for time comparisons
% Select one row per satellite (closest to startTime)
svIDs = unique(navicRinex.SatelliteID);
selectedRows = [];
for i = 1:numel(svIDs)
svRows = navicRinex(navicRinex.SatelliteID == svIDs(i), :);
[~, idx] = min(abs(svRows.Time - startTime));
selectedRows = [selectedRows; svRows(idx,:)]; %#ok<AGROW>
end
sc = satelliteScenario;
sc.StartTime = startTime;
sc.StopTime = sc.StartTime + seconds(waveDuration - stepTime);
sc.SampleTime = stepTime;
HelperAddSatellite(sc, selectedRows);
sat = sc.Satellites; % HelperAddSatellite modifies sc in-place, returns nothing useful
rx = groundStation(sc, lat, lon, Altitude=alt);
rx.MinElevationAngle = 10;Gotcha: HelperAddSatellite adds satellites to the scenario object
in-place. It does NOT return satellite objects — use sc.Satellites to get
the satellite array for dopplershift() and latency() calls.
The NavIC constellation has GEO and GSO satellites, so propagation delays are longer than GPS/Galileo MEO satellites (119-130 ms vs 67-86 ms).
Transmit power for SNR calculation: Pt = 50 W.
Step 4: Encode Navigation Data
HelperNavICRINEX2Config takes the RINEX data table directly (not a
filename):
navicCfg = HelperNavICRINEX2Config(selectedRows);
tempnavdata = HelperNavICDataEncode(navicCfg(1));
navdata = zeros(length(tempnavdata), length(navicCfg));
navdata(:,1) = tempnavdata;
for isat = 2:length(navicCfg)
navdata(:,isat) = HelperNavICDataEncode(navicCfg(isat));
endNav data produces 7200 rows per satellite. Only the first numsteps
rows are consumed.
Step 5: Generate Waveform
Using HelperNavICBBWaveform (Recommended)
HelperNavICBBWaveform wraps the manual modulation. It returns a
multi-column matrix (one column per satellite). Pass this directly to
HelperGNSSChannel -- do NOT sum columns first.
visiblePRN = selectedRows.SatelliteID(satIndices)';
numsteps = round(waveDuration / stepTime);
samplesPerStep = sampleRate * stepTime;
gnsschannelobj = HelperGNSSChannel( ...
FrequencyOffset=dopShifts(1,satIndices), ...
SignalDelay=ltncy(1,satIndices), ...
SignalToNoiseRatio=snrs(1,satIndices), ...
SampleRate=sampleRate, ...
RandomStream="mt19937ar with seed", Seed=73);
navicWaveform = zeros(numsteps * samplesPerStep, 1);
for istep = 1:numsteps
% Returns samplesPerStep x numSat matrix
bbWave = HelperNavICBBWaveform( ...
navdata(istep,satIndices), "NavIC L5-SPS", [], visiblePRN, sampleRate);
idx = (istep-1)*samplesPerStep + (1:samplesPerStep);
navicWaveform(idx) = gnsschannelobj(bbWave);
if istep < numsteps
gnsschannelobj.SignalToNoiseRatio = snrs(istep+1, satIndices);
gnsschannelobj.FrequencyOffset = dopShifts(istep+1, satIndices);
gnsschannelobj.SignalDelay = ltncy(istep+1, satIndices);
end
endL1 SPS Waveform Generation (Using Helper)
L1 has a different calling convention — loop per satellite with bitIdx:
% Ensure L1 support files are loaded
L1PRNInit = load("NavICL1SpreadingCodeInitialConditions.mat");
navicWaveform = zeros(numsteps * samplesPerStep, 1);
for istep = 1:numsteps
bbWaveAll = zeros(samplesPerStep, numel(visiblePRN));
for isat = 1:numel(visiblePRN)
bbWaveAll(:,isat) = HelperNavICBBWaveform( ...
navdata(istep,satIndices(isat)), "NavIC L1-SPS", ...
L1PRNInit, visiblePRN(isat), sampleRate, istep);
end
idx = (istep-1)*samplesPerStep + (1:samplesPerStep);
navicWaveform(idx) = gnsschannelobj(bbWaveAll);
if istep < numsteps
gnsschannelobj.SignalToNoiseRatio = snrs(istep+1, satIndices);
gnsschannelobj.FrequencyOffset = dopShifts(istep+1, satIndices);
gnsschannelobj.SignalDelay = ltncy(istep+1, satIndices);
end
endManual Modulation (Without Helper)
For L5-SPS/S-SPS, the manual pattern uses gnssCACode, bocmod, and
interplexmod. Note: gnssCACode returns int8 -- cast to double
before passing to interplexmod.
% Per-satellite spreading + modulation
caCode = double(gnssCACode(prnID, "NavIC L5-SPS")); % 1023x1, int8->double
SPSBits = xor(repmat(caCode, 20, 1), navDataBit); % 20 repetitions per bit
SPSmod = 1 - 2*double(SPSBits);
% Rate-match SPS with BOC-modulated RS signals
s2 = repelem(SPSmod, 10, 1); % SPS
s1 = bocmod(dummyRSD, 5, 2); % RS-Data
s3 = bocmod(dummyRSP, 5, 2); % RS-Pilot
A = [2/3, sqrt(2)/3, sqrt(2)/3];
[compositeSig, efficiency] = interplexmod([s1, s2, s3], A);R2026b: navicWaveformGenerator
When navicWaveformGenerator ships in R2026b, the NavIC pipeline will
simplify to match GPS/Galileo:
% R2026b pattern (not yet available)
wavegenobj = navicWaveformGenerator(SampleRate=sampleRate);
wavegenobj.SignalType = "L5";
wavegenobj.SVID = visibleSVIDs;
% ... same stepped loop as GPS/Galileo ...Until then, use HelperNavICBBWaveform or manual modulation.
Key Differences from GPS/Galileo
| Aspect | GPS/Galileo | NavIC (R2026a) |
|---|---|---|
| System object | Yes | No (manual modulation or helper) |
| Orbit type | MEO (~20,200 km) | GEO/GSO (~35,786 km) |
| Propagation delay | 67-86 ms | 119-130 ms |
| Scenario setup | satellite(sc, data) | HelperAddSatellite(sc, rinexTable) |
| Nav data source | Almanac or RINEX | RINEX only |
| Nav config helper | HelperGPSAlmanac2Config / HelperGalileoNavigationData | HelperNavICRINEX2Config(rinexTable) |
| Transmit power | 44.8 W (GPS) / 160 W (Galileo) | 50 W |
| Time conversion | HelperGPSConvertTime (GPS) | HelperGNSSConvertTime |
gnssCACode type | "GPS" | "NavIC L5-SPS" or "NavIC S-SPS" |
| Channel input | Single column (system object combines) | Multi-column (one per satellite) |
Copyright 2026 The MathWorks, Inc.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
The author says: >=R2026a
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- MathWorks
- Publisher
- MathWorks (Official)
- Source
- matlab/matlab-agentic-toolkit · skills-catalog/wireless-communications/matlab-generate-gnss-waveform · commit 2a7f215
- Licence
- BSD 2-Clause License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by MathWorks.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-generate-gnss-waveform.zip