Using a Cheap USB GPS Module (VK-162) with Python

I recently picked up a VK-162 USB GPS module from AliExpress for about $8. It’s a simple, plug-and-play GPS receiver that presents itself as a serial device (on Windows, it shows up as a COM port) as opposed to most cheap GPS units, which only offer a UART output.

This same device is also known as the “G-Mouse USB GPS dongle”

Surprisingly, when I went looking for examples on how to actually read and parse data from this module, I didn’t find much. So I put together a small script that works reliably, and I’m sharing it here in case it helps someone else.


Features

  • Built in Ublox G6010 / G7020 GPS chipset.
  • 50 channel GPS L1 frequency C/A Code.
  • Superior sensitivity up to -162dBm.
  • Built-in WAAS/EGNOS/MSAS Demodulator without any additional hardware.
  • Low power consumption.
  • Waterproof design.
  • USB interface with a 2m long cable.
  • Magnetic base.
  • Compatible with Linux & Windows.

Specifications

  • Tracking sensitivity: -160dBm
  • Acquisition sensitivity: -146dBm
  • Cold start time: 32s [average]
  • Warm start time: 32s [average]
  • Hot start time: 1s [average]
  • Recapture Time: 0.1s [average]

How It Works

When you connect the VK-162 to your PC and open the COM port (e.g., with PuTTY), you’ll see NMEA sentences streaming in, such as:

$GPRMC,,V,,,,,,,,,,N*53
$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,*48

Although that’s valid GPS data, it’s not very user-friendly and requires further processing to be human-readable.

NMEA definitions:

$GPVTG → Course and speed over ground.
$GPRMC → Recommended Minimum GPS data (time, status, lat/lon, speed, date).
$GPGGA → Fix data (quality, satellites, altitude).
$GPGSA → DOP (dilution of precision) and active satellites.
$GPGLL → Geographic position.


Parsing NEMA Sentences

With Python and a couple of libraries (pyserial and pynmea2), you can parse these NMEA sentences and present them in a more readable way.

Note: I added a simple configuration flag for RAW_OUTPUT so you can choose whether to display the raw NMEA data alongside the parsed output, in case it’s not working as expected.

Install dependencies before running:

pip install pyserial pynmea2

Python Script

The example Python script will parse the NEMA sentences into a human readable output.

Configuration

At the top of the script, in the Config section, there’s configuration variables to set the the PORT & if RAW_OUTPUT status:

# --- Config ---
PORT = "COM9"
RAW_OUTPUT = False   # Set True to show raw NMEA sentences
BAUDRATE = 9600
# ---------------

You MUST set the correct PORT prior to running the script.

In most cases, the device will be listed as a USB Serial Device in your Windows Device Manger.

Example Code

import serial
import pynmea2

# --- Config ---
PORT = "COM9"
RAW_OUTPUT = False   # Set True to show raw NMEA sentences
BAUDRATE = 9600
# ---------------

def main():
    ser = serial.Serial(PORT, baudrate=BAUDRATE, timeout=1)
    print(f"Listening to GPS on {PORT}... (Ctrl+C to stop)")

    while True:
        try:
            line = ser.readline().decode('ascii', errors='replace').strip()
            if not line.startswith('$'):
                continue

            if RAW_OUTPUT:
                print(f"RAW: {line}")

            try:
                msg = pynmea2.parse(line)

                if isinstance(msg, pynmea2.types.talker.RMC):
                    print(f"[RMC] Time: {msg.datestamp} {msg.timestamp}")
                    print(f"      Status: {'Active (fix)' if msg.status == 'A' else 'Void (no fix)'}")
                    print(f"      Lat/Lon: {msg.latitude}, {msg.longitude}")
                    print(f"      Speed (knots): {msg.spd_over_grnd}, Course: {msg.true_course}")
                    print("-" * 40)

                elif isinstance(msg, pynmea2.types.talker.GGA):
                    print(f"[GGA] Fix Quality: {msg.gps_qual}")
                    print(f"      Satellites: {msg.num_sats}")
                    print(f"      Altitude: {msg.altitude} {msg.altitude_units}")
                    print("-" * 40)

                elif isinstance(msg, pynmea2.types.talker.GSA):
                    print(f"[GSA] Mode: {msg.mode}, Fix Type: {msg.mode_fix_type}")
                    print(f"      PDOP: {msg.pdop}, HDOP: {msg.hdop}, VDOP: {msg.vdop}")
                    print("-" * 40)

            except pynmea2.nmea.ParseError:
                continue

        except KeyboardInterrupt:
            print("Exiting...")
            break

if __name__ == "__main__":
    main()

Example Output

Here’s what the script looks like in action.

Debug mode on (RAW_OUTPUT = True)

Listening to GPS on COM9... (Ctrl+C to stop)
RAW: $GPRMC,233029.00,A,3548.56817,N,08229.00148,W,0.045,,160925,,,A*68
[RMC] Time: 2025-09-16 23:30:29+00:00
      Status: Active (fix)
      Lat/Lon: 35.8094695, -82.483358
      Speed (knots): 0.045, Course: None
----------------------------------------
RAW: $GPVTG,,T,,M,0.045,N,0.084,K,A*2E
RAW: $GPGGA,233029.00,3548.56817,N,08229.00148,W,1,08,1.05,762.2,M,-32.5,M,,*68
[GGA] Fix Quality: 1
      Satellites: 08
      Altitude: 762.2 M
----------------------------------------
RAW: $GPGSA,A,3,31,26,16,03,09,27,04,08,,,,,1.95,1.05,1.64*0A
[GSA] Mode: A, Fix Type: 3
      PDOP: 1.95, HDOP: 1.05, VDOP: 1.64
----------------------------------------
RAW: $GPGSV,3,1,09,03,16,218,21,04,79,254,33,08,30,176,31,09,42,311,33*72
RAW: $GPGSV,3,2,09,16,71,030,14,26,32,051,26,27,49,139,34,28,01,100,*7D
RAW: $GPGSV,3,3,09,31,25,088,30*46
RAW: $GPGLL,3548.56817,N,08229.00148,W,233029.00,A,A*79
RAW: $GPRMC,233030.00,A,3548.56819,N,08229.00153,W,0.058,,160925,,,A*68
[RMC] Time: 2025-09-16 23:30:30+00:00
      Status: Active (fix)
      Lat/Lon: 35.80946983333333, -82.48335883333333
      Speed (knots): 0.058, Course: None
----------------------------------------

Debug mode off (RAW_OUTPUT = False)

Listening to GPS on COM9... (Ctrl+C to stop)
[RMC] Time: 2025-09-16 23:31:17+00:00
      Status: Active (fix)
      Lat/Lon: 35.80946866666667, -82.48338616666666
      Speed (knots): 0.112, Course: None
----------------------------------------
[GGA] Fix Quality: 1
      Satellites: 08
      Altitude: 757.2 M
----------------------------------------
[GSA] Mode: A, Fix Type: 3
      PDOP: 1.94, HDOP: 1.05, VDOP: 1.63
----------------------------------------

No GPS Fix

If your device has not yet completed the GPS fix, you will see Status: Void.
—————————————-
Raw: $GPGSV,1,1,01,16,,,25*78
Raw: $GPGLL,,,,,,V,N*64
Raw: $GPRMC,,V,,,,,,,,,,N*53
Time: None None
Status: Void (no fix)
Lat/Lon: 0.0, 0.0
Speed (knots): None, Course: None
—————————————-

It can take up to 180 seconds to achieve a fix from a cold boot; make sure the device is near a window and has a line of sight to the outside sky.


Conclusion

For less than $10, this little VK-162 USB GPS dongle works great with Python. By parsing the NMEA output with pynmea2, you can easily log your position, altitude, and satellite info, or even integrate it into larger projects like mapping or robotics.

Sometimes the cheap modules on AliExpress end up being surprisingly useful — and with a bit of code, you can unlock their potential.


Additional Resources

https://www.instructables.com/GPS-Raspberry-Pi

Leave a Reply

Your email address will not be published. Required fields are marked *