A two-character fix in two hours: Wrestling with gpsd, cgps, and a $15 GPS puck

December 2025 Update

TL;DR The $15 GPS dongle is generating severe RF noise.

I was tracking down some QRM (man-made interference) in my sBitx radio that was especially bad on 10m, but still present on 15 and 20 meters. After returning from a portable activation, I noticed that the noise was gone. Then I noticed that I had not plugged in the GPS dongle. Plugged in the dongle, QRM was back. Unplugged it, QRM gone!

Searching online, I found this discussion about cheap VK-162-based GPS dongles from Amazon creating QRM.

I have tested 3 of the VK-162's...with u-blox drivers on both Win10 and Win11. So far, the VK-162s, while cheap and VERY sensitive, are also VERY DIRTY on HF for broadband noise. At the same time, using the same cables, the traditional hockey pucks (now $59 on Amazon, where they were $19 a few short years ago), produce no noise that I can detect. Conclusion: I wouldn't recommend the VK-162 at $12.00 to any Ham, unless someone finds the "magic cure" to this severe RFI.


Full disclosure: I used ChatGPT to reformat my troubleshooting notes into the first draft of this post. Extensive checking and rewriting followed.
TL;DR: If your cheap VK-162 GPS puck shows correct coordinates in gpsmon but cgps is off by ~1 arcminute, the culprit is gpsd probing the device into binary mode and mis-parsing.
Fix: add -b to GPSD_OPTIONS in /etc/default/gpsd so gpsd sticks to NMEA-only. Then restart gpsd, and everything lines up with your phone's GPS and mapping program again.

Sometimes the cheap gadgets surprise you. Other times, they try to gaslight you.

I decided to add a GPS reciever to my sBitx V3 HF transceiver, which I've started to use for portable ops and POTA activations. WSJT-based digital modes, like FT8, FT4, and JS8Call, need the system time to be accurate to one second to operate optimally, so this would set the clock from GPS time (good to a few msec, without the 1 PPS signal) when the radio doesn't have access to the Internet and the usual pool of NTP servers. It would also let me set the Maidenhead grid locator easily, which is also important to WSJT protocols. The sBitx radio is run by a Raspberry Pi, so all the software to do this is just an apt-get away. I run Chrony and a Wifi hotspot, so could also serve accurate time to other nearby computers, say during ARRL Field Day.

Easy peesy, right? I overnighted a little VK-162 USB GPS dongle ($15 on Amazon, with many five-star reviews). Hooked it up, fired up the usual gpsd clients… and immediately hit a head-scratcher:

  • gpsmon showed my position correctly.
  • cgps was off — by about one arcminute in both latitude and longitude.

That’s about 1.8 km of error. GPS with a favoriable sattelite constellation should be good to a few meters. 1.8 km is not a rounding error, not DMS vs decimal degrees — just wrong. Note that gpsmon agreed with my iPhone and coordinates read off of Google Earth, while cgps didn’t.

Peeking under the hood

First step: dump the raw NMEA sentences directly from the dongle:

gpspipe -r -n 10 | grep GPGGA

This shows $GPGGA strings — the classic “latitude/longitude in NMEA format” messages. NMEA format uses DDM -- that's degrees and decimal mintutes for latitude and longitude, not decimal degrees or DMS, degrees, minutes, seconds. For example, my longitude appears in the NEMA sentence as 12217.87589, read that as 122 degrees and 17.87589 minutes (crazy, I know). To convert to decimal degrees, divide the minutes by 60 and add to the degrees: 122 + (17.87589/60) = 122.2979315 degrees. When I converted those by hand, they matched cgps, not gpsmon.

Wait… what? Why are the coordinates in the NMEA sentences wrong?

Remember: gpsmon, which is showing the correct values, talks directly to the serial device, while gpspipe and cgps go through gpsd. Reading the "-r" option on gpspipe's man page, we see

 
  -r, --nmea
           Cause NMEA sentences to be output. This may be NMEA, pseudo NMEA
           built from binary data, or some combination of both.
  
Read that again, pseudo NMEA built from binary data. Huh? What binary data????

After a bunch of Googling, I learned that many GPS chipsets (especially u-blox) speak both NMEA and proprietary binary languages. gpsd's default behavior is to poke the dongle and say, “Hey, let’s talk UBX instead of NMEA!” My little VK-162 happily says “sure,” but then the buggy firmware in the ublox-knockoff chipset produces slightly non-compliant (Yeah, I know, that's like saying "slightly pregnant") binary data in some cases and then gpsd mis-parses the binary stream, resulting in those ~1′ errors. The NEMA sentences were wrong because they were being built by gpsd from mis-parsed binary data, not directly from the GPS!!

The magic flag

After a lot of head-scratching and beard stroking, I found that newer versions of gpsd (3.24+) paperover the buggy binary data from ublox knockoffs. So one fix is to upgrade to the latest version of gpsd, only problem is that the Raspberry Pi repos have 3.22. I know about backports to get newer versions, but that complicates future apt upgrades, and I don't want to go there. The breakthrough came from further study of the gpsd man page and running gpsd manually:


sudo systemctl stop gpsd
sudo systemctl restart gpsd.socket
sudo gpsd -N -n -D 4 -F /var/run/gpsd.sock -b /dev/gps0

The magic bit? -b. Two characters. That's b is for broken, by the way.

That flag tells gpsd: don’t try to probe the device for a binary protocol.

With -b, gpsd leaves the dongle alone, sticks to plain NMEA, and suddenly cgps matches gpsmon, my phone, and Google Earth perfectly.

Making it stick on Raspberry Pi OS

On Raspbian (and other Debian-based distros), gpsd is started via systemd with settings in /etc/default/gpsd. To make the fix permanent:

# /etc/default/gpsd
START_DAEMON="true"
GPSD_OPTIONS="-n -b"  # <--- the magic -b
DEVICES="/dev/gps0"
USBAUTO="true"
GPSD_SOCKET="/var/run/gpsd.sock"

Restart gpsd:

sudo systemctl restart gpsd

…and now cgps behaves after every reboot. Wooohooooo!

Troubleshooting Recipe

If you ever hit the same “gpsmon vs cgps mismatch,” here’s a step-by-step way to track it down:

  1. Check gpsmon vs cgps. If they disagree by about 1′, you’re in familiar territory.
  2. Dump raw NMEA:
    gpspipe -r -n 10
    
    Look for $GPGGA lines.
  3. Convert manually to decimal degrees, for example:
    4916.45,N → 49°16.45′ = 49 + 16.45/60 = 49.2742°
    12311.12,W → 123°11.12′ = -(123 + 11.12/60) = -123.1853°
  4. Compare to gpsd JSON:
    gpspipe -w -n 5
    
    If gpsd’s numbers differ from the manual conversion, gpsd is mis-parsing.
  5. Try gpsd with -b:
    sudo systemctl stop gpsd
    sudo gpsd -N -n -D 4 -F /var/run/gpsd.sock -b /dev/gps0
    
    See if cgps suddenly agrees with gpsmon.
  6. If it works, make -b permanent in /etc/default/gpsd.

Why this happens

  • gpsmon: dumb but honest — it shows whatever the dongle spits out in NMEA.
  • gpsd (without -b): “helpfully” switches the dongle into binary mode, then misreads the data.
  • cgps: only knows what gpsd tells it, so it looked wrong.

Lessons learned

  1. Cheap hardware + old software = mystery bugs. My Pi runs gpsd 3.22 (the latest in Raspberry Pi OS repos). This bug is already fixed in newer gpsd releases, but Pi OS hasn’t caught up.
  2. Trust the reference. If your GPS says you’re in the next town over but your phone agrees with reality, don’t gaslight yourself.
  3. -b is your friend. For low-cost dongles, forcing NMEA-only mode can save a lot of hair-pulling.

So if your $15 dongle is lying to you: don’t throw it in the drawer yet. Just tell gpsd to quit being clever.

Sidebar: What’s Inside the VK-162?

Pop open a VK-162 (or read the teardowns online), and you’ll usually find a u-blox 7 or 8 clone chipset on a tiny board, paired with a ceramic patch antenna. It’s basically a knock-off of a u-blox USB puck, but sold under a dozen crazy-sounding brand names.

That explains the quirks:

  • They speak UBX binary as well as NMEA, but the firmware isn’t always fully spec-compliant.
  • gpsd probing tickles UBX mode → bad coordinates.
  • Forcing NMEA-only with -b sidesteps the whole mess.

It also explains why these dongles “just work” with simple apps (they only care about NMEA), but sometimes misbehave with more sophiscated tools, like gpsd.

Further Reading / References

Comments

Popular posts from this blog

New BBC Radio 3 streams

Further adventures of AK6IM, the kosher ham