Skip to navigation

Setting up GNSS / RTK on Windows

Open in ClaudeOpen in ChatGPT

This guide explains how to connect a GNSS receiver to a Windows computer and read its data in a ZED SDK application. If you are new to GNSS, start with the GNSS Overview.

gpsd, used in the Linux guide, does not run natively on Windows. You can either read the NMEA sentences directly from the serial port of the receiver, as described below, or run gpsd in WSL 2 to reuse the Linux workflow.

Connecting the receiver

Connect the antenna and the receiver, then open the Device Manager and expand Ports (COM & LPT). The receiver appears as a COM port, for example COM3:

  • Receivers with a native USB interface usually use the generic USB Serial Device driver included in Windows 10 and Windows 11.
  • Receivers connected through a USB-to-serial adapter may need the driver of the adapter manufacturer.
  • Some receiver vendors provide a driver that exposes the receiver as a Windows location sensor instead of a COM port. To read the data from your application, install the virtual COM port driver of the receiver.

For receivers connected through a serial adapter, check the baud rate configured on the receiver: your application must use the same value.

Checking the data

You can check that the receiver streams NMEA sentences with a serial terminal, or with the following Windows PowerShell commands:

$port = New-Object System.IO.Ports.SerialPort COM3,115200,None,8,One
$port.Open()
while ($true) { $port.ReadLine() }

Press Ctrl+C to stop, then release the port with $port.Close().

Replace COM3 and 115200 with the COM port and the baud rate of your receiver. On Windows, a COM port can be opened by only one application at a time: close the terminal before starting your application.

The output should be similar to the one shown in the Linux guide. The position fields stay empty until the receiver gets a fix.

Enabling RTK

To get RTK accuracy, an NTRIP client must receive the corrections from an NTRIP caster and send them to the receiver. On Windows, you can use:

  • the configuration software of your receiver vendor, which often includes an NTRIP client,
  • a standalone NTRIP client, for example the open-source RTKLIB tools.

Configure it with the NTRIP connection information of your correction provider.

Since a COM port cannot be shared, the NTRIP client and your application cannot open the same port. Depending on your receiver and your NTRIP client, you can:

  • send the corrections and read the NMEA sentences on two different ports of the receiver, for example the USB port and a UART through a serial adapter,
  • use an NTRIP client that forwards the data of the receiver to a TCP port, and read the NMEA sentences from this TCP port in your application.

To benefit from the RTK accuracy, wait until the receiver reports an RTK fixed solution (GGA fix quality 4) before starting your application.

Reading GNSS data in your application

The example below reads the NMEA sentences of the receiver with pyserial, decodes them with pynmea2, and converts them to sl.GNSSData:

  • the GGA sentence provides the position and the fix quality,
  • the GST sentence provides the standard deviation of the position, used to fill the covariance.

Install the dependencies with:

pip install pyserial pynmea2
Python
import serial
import pynmea2
import pyzed.sl as sl
# GGA fix quality to sl.GNSS_STATUS
GGA_STATUS = {1: sl.GNSS_STATUS.SINGLE, 2: sl.GNSS_STATUS.DGNSS,
4: sl.GNSS_STATUS.RTK_FIX, 5: sl.GNSS_STATUS.RTK_FLOAT}
class NMEAReader:
def __init__(self, port, baudrate=115200):
self.serial = serial.Serial(port, baudrate, timeout=1)
self.gga = None # last GGA sentence and its reception time
self.gst = None # last GST sentence
def read(self):
"""Read one NMEA sentence. Return a sl.GNSSData when a complete measurement is available, None otherwise."""
line = self.serial.readline().decode("ascii", errors="ignore").strip()
if not line.startswith("$"):
return None
try:
msg = pynmea2.parse(line)
except pynmea2.ParseError:
return None
if msg.sentence_type == "GGA":
self.gga = (msg, sl.get_current_timestamp())
elif msg.sentence_type == "GST":
self.gst = msg
else:
return None
# Wait for the GGA and GST sentences of the same measurement
if self.gga is None or self.gst is None or self.gga[0].timestamp != self.gst.timestamp:
return None
gga, reception_ts = self.gga
gst = self.gst
self.gga = self.gst = None
if gga.gps_qual not in GGA_STATUS or gga.altitude is None:
return None # no valid fix
if None in (gst.std_dev_latitude, gst.std_dev_longitude, gst.std_dev_altitude):
return None # no uncertainty estimate
gnss_data = sl.GNSSData()
gnss_data.set_coordinates(gga.latitude, gga.longitude, gga.altitude, False)
gnss_data.latitude_std = gst.std_dev_latitude
gnss_data.longitude_std = gst.std_dev_longitude
gnss_data.altitude_std = gst.std_dev_altitude
# Covariance in the ENU frame: East (longitude), North (latitude), Up (altitude)
gnss_data.position_covariances = [gst.std_dev_longitude ** 2, 0.0, 0.0,
0.0, gst.std_dev_latitude ** 2, 0.0,
0.0, 0.0, gst.std_dev_altitude ** 2]
gnss_data.ts = reception_ts
gnss_data.gnss_mode = sl.GNSS_MODE.FIX_3D.value
gnss_data.gnss_status = GGA_STATUS[gga.gps_qual].value
return gnss_data

Call read() in a dedicated thread, and ingest each returned sl.GNSSData into the Fusion module with ingest_gnss_data, as shown in the Global Localization overview.

Not all receivers output the GST sentence by default. Enable it with the configuration software of your receiver. If your receiver cannot output it, use its proprietary messages to get the position uncertainty: do not ingest a constant covariance.

Timestamps

The example timestamps each measurement with its reception time, given by sl.get_current_timestamp(). This keeps GNSS data and camera images on the same time base, even if the Windows clock is not precisely synchronized with UTC, at the cost of the latency of the receiver and of the serial link, typically a few tens of milliseconds.

The time contained in the NMEA sentences can be used instead only if the Windows clock is accurately synchronized with UTC. With its default configuration, the Windows time service does not guarantee this level of accuracy. See Using GNSS Data in Your Application for more details.

Alternative: running gpsd in WSL 2

If you prefer to use gpsd, including its NTRIP client, you can run it in WSL 2 (Windows Subsystem for Linux) and attach the USB receiver to it with usbipd-win:

  1. Install usbipd-win from an administrator PowerShell:

    winget install --interactive --exact dorssel.usbipd-win
  2. List the USB devices and note the BUSID of the receiver:

    usbipd list
  3. Share the device, then attach it to WSL. The bind command requires administrator rights and is needed only once per device:

    usbipd bind --busid <BUSID>
    usbipd attach --wsl --busid <BUSID>
  4. In WSL, check that the receiver is detected as /dev/ttyACM0 or /dev/ttyUSB0, then install and run gpsd as described in the Linux guide. Keep the -G option of the gpsd commands, which allows connections from Windows.

Your Windows application can then read the data from gpsd on localhost, port 2947, for example with the gpsdclient Python library shown in Using GNSS Data in Your Application.

The device must be attached again with usbipd attach every time it is unplugged or the computer restarts, and gpsd must be started again in WSL. As above, timestamp the data with its reception time unless the Windows clock is synchronized with UTC.