USB more accurate than GPIO UART?

Got myself a cheap GPS module that I’ve hooked up to my Raspberry Pi 3’s GPIO pins for serial and PPS.

Spent a lot of time getting it working, rearranging the cables for the different GPIO pins to get it right, and trying to power the GPS module using USB for troubleshooting. Turns out, the GPS module can do NMEA over USB as well, and the time Chrony receives over GPIO is >100ms off, while it seems about right over USB. How can that be? Am I doing something wrong?

chronyc -n sources

MS Name/IP address         Stratum Poll Reach LastRx Last sample
#x GPIO                          0   4   377    19   +126ms[ +126ms] +/-  401us
#+ USB                           0   4   377    20    +40ns[ +129ns] +/-  817ns
#* PPS                           0   4   377    20    +40ns[ +129ns] +/-  817ns
^?                2   6   377   435    -92us[  -92us] +/- 4445us
^?                  3   6   377    54  +2356us[+2356us] +/-   23ms
^?                 2   6   377    46  +5935us[+5935us] +/-   11ms
^?                2   6   377    45  +6133us[+6133us] +/-   10ms

relevant bits of chrony.conf

refclock SHM 0 refid GPIO
refclock SHM 2 refid USB trust
refclock PPS /dev/pps0 refid PPS lock USB trust

bits from /boot/config.txt

# Uncomment some or all of these to enable the optional hardware interfaces

# Uncomment this to enable infrared communication.
# the next 3 lines are for GPS PPS signals


# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.

DEVICES="/dev/ttyAMA0 /dev/pps0"

# Other options you want to pass to gpsd

# Automatically hot add/remove USB GPS devices via gpsdctl

Probably because as per the gpsd configuration you shared, the USB input is actually taking the PPS input into account.

I did the same with the GPIO input as well earlier, and it didn’t help any, still >100ms off

What do you mean by that, i.e., what is “the same”?

As per the gpsd configuration you shared, gpsd is processing three inputs, and providing output based on that: The UART, the GPIO PPS device, and the USB device (I assume that one is dynamically added).

Sorry, I though you were referring to “lock USB” in the Chrony config, just now realised you wrote gpsd config, not Chrony config. Either way, the NMEA time coming in through the UART are >100ms off, with or without USB.

Not sure how the USB takes the PPS into account in my config?

For the record, ttyAMA0 is UART, ttyACM0 is USB.

SHM 0 is NMEA (or binary) time without PPS.

SHM 2, with the gpsd Magic HAT kludge enabled (triggered by giving gpsd the ttyAMA name and having a pre-existing pps0 when gpsd starts), has time including PPS. (Without the kludge, it would be on SHM 1.)

ttyAMA is statically configured in gpsd. ttyACM gets added dynamically at runtime (the USBAUTO setting).

So you have two non-PPS sources for gpsd, plus PPS.

Not sure how the multiple non-PPS sources are processed to combine for the (single) output, I always avoided that ambiguity (if only to not have to figure out what gpsd does in this case).

I.e., what chronyd picks up is “some” non-PPS time via SHM 0, somehow derived from the two non-PPS inputs. Plus the PPS-enhanced time via SHM 2.

I.e., you labeled them on the chronyd side, but not sure the labels are actually correct (depending on how gpsd derives time from the multiple non-PPS inputs). E.g., could be that you’re actually getting the “time” (as in seconds numbering, vs. the PPS pulse) from the UART in both cases (i.e., both SHMs), and USB is ignored. Or USB only. Or some mixture of both. On both SHMs (which differ only in having PPS merged in as well, or not).

I.e., the labels are misleading, because the crucial difference is that SHM 2 already has PPS merged in (similar to what chronyd does based on the “lock” option). And SHM 0 is only based on the data stream (NMEA or binary, e.g., u-blox binary), without the PPS. Not that one is from UART, and the other from USB.

So you’re actually feeding the same PPS signal to chronyd twice, once directly, once through gpsd.

Oh ok, I just assumed SHM 0 was UART, SHM 1 was PPS through gpsd and SHM 2 was USB.
I’ll look for some documentation. Thanks!

Yeah, that would be the “normal” way, if the Magic HAT kludge weren’t active.

Normally, SHM 0 and SHM 2 are non-PPS, SHM 1 and SHM 3 with PPS. SHM 0 and 1 require privileges to access, SHM 2 and 3 don’t. So you could feed time from two separate gpsd instances into chronyd et al., one instance started as root, the other as non-root (though not sure off the top of my head whether you’d get KPPS in the latter case).

I haven’t tried it, but a comment in the gpsd code reads

Segments are allocated to activated devices on a first-come-first-served basis.

Plus some more details, e.g., that they are allocated in pairs (“coarse” time and PPS-enhanced time, respectively), and the privileged/unprivileged part.

Sounds like your thought/expectation would generally have been correct, wouldn’t the kludge have changed the default allocation.

Have you tried running ntpshmmon (as root, to see all active SHMs)? Would be interesting to see whether gpsd is maybe simply skipping the pair of SHMs that includes SHM 2 now occupied by the kludge, and puts the USB device (as assumedly the second one added to gpsd) into the SHM 4/5 pair, or any other SHM.

What am I looking at?

badeand@raspberrypi3:~ $ sudo ntpshmmon
ntpshmmon: version 3.22
#      Name  Seen@                 Clock                 Real                 L Prc
sample NTP1  1712952288.088861630  1712952285.994255883  1712952286.000000000 0 -20
sample NTP3  1712952288.088994807  1712952286.051107387  1712952285.999682782 0 -20
sample NTP0  1712952290.117786333  1712952290.117690604  1712952289.999684070 0 -20
sample NTP2  1712952290.995190528  1712952290.994256986  1712952291.000000000 0 -20
sample NTP0  1712952291.115769219  1712952291.115320573  1712952290.999684392 0 -20
sample NTP2  1712952291.995362114  1712952291.994255081  1712952292.000000000 0 -20
sample NTP0  1712952292.116701794  1712952292.116389398  1712952291.999684712 0 -20
sample NTP1  1712952292.995387188  1712952292.994256146  1712952293.000000000 0 -20
sample NTP2  1712952292.995484324  1712952292.994256146  1712952293.000000000 0 -20
sample NTP0  1712952293.112412439  1712952293.112296241  1712952292.999685031 0 -20
sample NTP1  1712952293.994563825  1712952293.994257314  1712952294.000000000 0 -20
sample NTP2  1712952293.994806637  1712952293.994257314  1712952294.000000000 0 -20
sample NTP0  1712952294.118842675  1712952294.117613351  1712952293.999685353 0 -20
sample NTP1  1712952294.998645124  1712952294.997645245  1712952295.000000000 0 -20
sample NTP2  1712952294.998746851  1712952294.997645245  1712952295.000000000 0 -20
sample NTP0  1712952295.118046243  1712952295.116935418  1712952294.999685671 0 -20

Gave it a quick try, and it seems to work!! (At least in the non-kludge case.)

Cool, thanks for triggering that I finally looked into this! :grinning:

Very interesting, thanks! You’re seeing how gpsd populates the different SHMs when the kludge is active: It seems the SHMs are just switched around a bit with respect to the non-kludge case, but everything expected is there:

NTP/SHM1 and 2 seem to be the PPS-enhanced times for each device (note the fractional part of the real time being zero - the assumption being the PPS signal by definition occurs precisely at the top of the second), and the other two SHMs seem to have the non-PPS times of the two devices.

Some oddities, though, so need to look at this a bit closer still, and think about this…

So, on one hand, it’s nice that it’s not just a case of me being stupid, but on the other hand, it’s frustrating that no guides I can follow or configs I can copy/paste from the internet will get it right in my case

I hear you. I guess the challenge is that there are so many different ways to do things, different preferences (e.g., what time daemon to use, and how to feed local time sources into it), and different know-how levels/perceptions what is considered challenging or easy, so there isn’t a one-stop shop for the solution. See recent discussions in other threads in this forum as case in point.

Not the one way to do it for the Raspberry Pi, and also spread across documents, and not only the bare-bones setup and config, but extensive explanations around (that may also confuse), but gpsd has some info that I find helpful, e.g.


for a hint at the “kludge”. Each with references to further information.

But all in all, I think you got it pretty much right, in the sense that you got the GPIO PPS working, and just a bit of misunderstanding on the gpsd side. :clap:

1 Like

So if I only add SHM 1 to Chrony, and no PPS, it’s pretty much as good as it’s gonna get?

MS Name/IP address         Stratum Poll Reach LastRx Last sample
#* NMEA                          0   4   377    11    -96ns[ -142ns] +/-  469ns
^?                0  10   377     -     +0ns[   +0ns] +/-    0ns
^?                  0  10   377     -     +0ns[   +0ns] +/-    0ns
^-                 2  10   377   479  +5496us[+5494us] +/-   11ms
^-                2  10   377   958  +5742us[+5736us] +/-   12ms

And the USB is unplugged now :slight_smile:

Thank you so much for you help :smiley:

Yeah, I think so. The (theoretical) benefit of passing PPS through gpsd is that with a u-blox device (which I infer you probably have from some of your description), gpsd claims to even take into account the PPS quantization error that some of those devices are able to report. Not sure though that matters at the precision the system is operating at.

Alternatively, feeding PPS directly into the time daemon is an option others prefer (especially with non-u-blox devices). One could run both in parallel and collect statistics to see whether there is a recognizable difference, but that is beyond what I ever tried, not sure anyone else has. Would be interesting, though. But again, not sure how relevant this would be, given the overall precision I guess is achievable. So to some extent a matter of personal preference and ambition.

The biggest issue with the RPi 3 and older is anyhow that the Ethernet port is attached via USB, so you get some jitter there when serving time to the outside world. Not sure again how much it matters in the grand scheme of things when serving time across wide area networks.

So I personally think those devices are great to get good time for the money, and quite easily (hook up and configure GPIO PPS, attach via USB to get power, and data into gpsd without fiddling with gpsd config, configure time daemon to pick up PPS directly, and coarse time from gpsd (to avoid fiddling with the UART both physically as well as from a configuration point of view)).

But you can obviously get better time with different setups, and people will have different views as to the “easy” part.

Pleasure, glad if others can benefit from the many hours figuring things out for myself :grin:.

It was cheap on Amazon, so I’m guessing it’s some random Chinese stuff. Doesn’t say U-blox on it

Right, forgot about that. I have a Pi4 as well, but the Pi3 was just conveniently available, and without a case that restricts access to the GPIO pins

Yeah, neither the board nor the module seem to be made by u-blox, but I bet there is a u-blox chip underneath the module cover, or a knock-off of a u-blox chip, and the module itself is a knock-off of a u-blox module. Not proof, but strong hints are the concurrent and independent UART and native USB interfaces (vs. USB from a UART-to-USB bridge), the module pinout found in some Amazon descriptions, the name GT-U7 hinting at u-blox 7 technology, and the general layout of the board (adapted to the module pinout). I guess that it not officially/legally being based on u-blox is why that’s not mentioned, but that’s speculation.

One can use e.g. gpsmon to see whether the module is speaking u-blox binary, and what version of hardware and firmware it is reporting.

I wasn’t saying anything against the RPi 3, have several in operation myself. Just suggesting that any effort getting the last picosecond accuracy out of the PPS might be lost if the purpose of using PPS is not something on the RPi itself, but serving external clients, especially over wide area networks.

I’ve noticed it says u-blox, yeah. Grabbed a screenshot right after starting gpsmon, before it puts up the tables

It’s serving local clients, mostly just my Windows PC, PfSense and the Pi4, though just NTP over the internet would be perfectly fine for this. Windows doesn’t even bother keeping close to NTP time, allowing the clock to drift significantly before syncing up again. Just a hobby project, so it’s alright :smiley: But it would be interesting knowing it can get more accurate time to NTP clients as well

Ok, thanks, interesting. I always wondered just a little bit about that with those modules.

Same here, even the offset due to the asymmetric line would be way below the accuracy I actually need. But it’s fun.

Meinberg provide a build of ntpd classic for Windows (with @davehart active in maintaining the Windows port, among other aspects), even though it is currently not based on the latest ntpd stable version released (but seems to have some selected patches from the latest release).

1 Like