beta-tester/RPi-GPS-PPS-StratumOne

Setup Trouble on Pi Zero 2 W with MAX-M8Q GNSS HAT

AlexLandherr opened this issue · 80 comments

Hi,

I tried setting up this solution for my NTP server running on a Pi Zero 2 W and using the MAX-M8Q GNSS HAT.
I am following this guide:
NEO-M8T_GNSS_TIMING_HAT, NTP_Server

The setup shell script runs fine but when I run:
watch -n1 chronyc sourcestats -v

I get this:
Skärmbild (17)

What could I be doing wrong?

hi, the guide is not from me.

it looks to my they use the wrong serial port. it must be /dev/ttyAMA0 when it is using the GPIO pins for serial port
and i use GPIO pin 4for PPS, but your module is using GPIO pin 18.
aou have to disable bluetooth. you have to enable Serial Port but disable serial console.

normally all this my script is doing ecxept of the GPIO pin 18.
but my script expects a fresh RasPi OS installation.

please try to use a fresh RasPi OS Bullseye installation, clone my project and run it.
in the /boot/config.txt file you have to change
dtoverlay=pps-gpio,gpiopin=4 # /dev/pps0
to
dtoverlay=pps-gpio,gpiopin=18 # /dev/pps0

Ok, thanks! I'll try it tomrrow.

Can I still set a static IPv4 address on the Pi Zero 2 W for my other computers on the same network so they can get the time from it?

yes, the script needs a working internet connection to install packages.
once everything of GPS and PPS is working it does not need internet anymore but you still can use it if you want.
the NTP service should be available to use for the other computers.

i told only to start with a fresh RasPi OS, because i don't know what changes were done on the system already.
if you still have problems, it is easier to find a solution.

once everything is running, you can also change/install other things...

I tried a fresh install of Raspberry Pi OS Lite 32-bit (release date 2021-10-30) and in the official imager used Ctrl+Shift+X to open the advanced settings menu to enable SSH and password for it along with the system time zone which I set to "Etc/UTC".

Upon having successfully logged in I ran sudo apt update and sudo apt full-upgrade and let those processes complete.
After that I installed git and cloned your repository, made the installation shell script executable and ran it allowing it to finish.

Subsequently I changed the line you said in /boot/config.txt to dtoverlay=pps-gpio,gpiopin=18 # /dev/pps0 as per instructions.

Then I set a static IPv4 address for the Pi according to this guide:
raspberry-pi-static-ip-address

And after that I rebooted.

When I ran watch -n1 chronyc sourcestats -v I got this again:
Skärmbild (19)

Based on my retelling of events should I just try again or am I missing something in your instructions?

do you get any output when you type in gpsmon ?
if you don't get coordinates from the gps there, please try to specify the baud rate of your gps manually in the /etc/default/gpsd file.
there is the line
GPSD_OPTIONS="-n -r -b"
append it with the -s <speed> (e.g. for a speed of 9600 baud)
GPSD_OPTIONS="-n -r -b -s 9600"
(if your GPS module uses a different speed, change the value according your device)

i guess the gpsd version of the distro is a one with broken autobaud mechanism.

to restart the gpsd and chrony services to take effect of the changes (without requireing rebooting the whole system):

sudo systemctl stop --now gpsd.{service,socket} && \
sudo systemctl restart --now chrony && \
sudo systemctl start --now gpsd

I got this from gpsmon:
Skärmbild (21)

ok, then the communication with the GPS module is not working.
did you already tried to specify the speed in the /etc/default/gpsd file ?

Yes, per the info here (I got the module from thepihut.com):
MAX-M8Q_GNSS_HAT_for_Raspberry_Pi_-_Specifications.pdf

And ran this version of your restart command:
sudo systemctl stop --now gpsd.{service,socket} && sudo systemctl restart --now chrony && sudo systemctl start --now gpsd

the speed of 9600 baud for serial port should be good
or did you set it up to another speed previously?

and you restarted the gpsd and chrony services / rebooted the system, and still no gps data?

I hadn't set it to another speed previously. And after restarting and running gpsmon I got the same result as per the image. A few weeks back I did remove the little battery holder for the HAT (with a soldering iron) as otherwise it wouldn't fit on the Pi Zero 2 W which has a little heatsink on it.

I did restart gpsd and chrony.

ok, without a battery i think it will always use the default 9600 baud.
can you check if you get a PPS signal at least...
with sudo ppstest /dev/pps0 you should get a periodic output (one line per second)

I got this recently:
pi@raspberrypi:~ $ sudo ppstest /dev/pps0 trying PPS source "/dev/pps0" found PPS source "/dev/pps0" ok, found 1 source(s), now start fetching data... time_pps_fetch() error -1 (Connection timed out) time_pps_fetch() error -1 (Connection timed out) ^C

It might take a while for a lock.

can you stop the gpsd service and socket to free the serial port with:
sudo systemctl stop --now gpsd.{service,socket}

and type in

stty -F /dev/ttyAMA0 9600 raw cs8 -cstopb -parenb
cat /dev/ttyAMA0

if you get some outout
if not, hit Ctrl-C to abort and try to change the baud rate to 115200 another common value

I got this after waiting some 20 minutes:
Skärmbild (23)

I even tried the extremes of the data rates listed here:
MAX-M8Q_GNSS_HAT_for_Raspberry_Pi_-_Specifications.pdf

slowly i run out of ideas.
i don't have a RPi Zero v2. maybe there is something different according serial port.

you can install the package minicom to try to get output from your serial port. but i guess your system is now running without the correct time/date, becacuse chrony is not using the internet to sync the system time with my configuration. so maybe installing packages is not possible anymore, because the time difference is too big.

to tell chrony to use time servers from the internet as well, please run the following commands:

sudo mv /etc/chrony/stratum1/20-ntp-servers.conf{.disabled,}
sudo systemctl restart --now chrony
sleep 10
chronyc -m tracking sources

as output you should see four additional sources from the internet

then sudo apt install minicom to install the minicom package and run
minicom -D /dev/ttyAMA0
try to get GPS output by changing the speed of the serial port

with [Ctrl]+[A],[Z] you can open the menu
with [Ctrl]+[A],[P] you can open the serial setup (to change the speed)
with [Ctrl]+[A],[X] you can cancel the program

The commands for minicom doesn't seem to be working over SSH. My SSH client on Windows 10 is PuTTY.
Skärmbild (25)

oh, as @ComputerSmiths told, without a battery on your GPS module at each powercycle it may take a very long time to get a gps fix. PPS will work only with a fix, NMEA data will normally come immediately with at least "empty" data items until a fix.

The commands for minicom doesn't seem to be working over SSH. My SSH client on Windows 10 is PuTTY.

then try to change the serial port speed in minicom, hit the keys [Ctrl] and [A] then [P].

The problem is that I get no response or indication that anything has happened on the SSH output window. Just that blank look and the row at the bottom. And I have not rebooted it for the last 1.5 - 2 hours.

If none of this works do you have a recommended Pi Zero W-friendly GPS HAT for your timing setup?

maybe the [Ctrl] key is not passing through Putty to the RPi. are there setting to pass through all control keys?
maybe you have more luck with the free version of MobaXTerm
or you try to start minicom with the speed given at commandline like:
minicom -D /dev/ttyAMA0 -b 9600 -8
and try out different speeds.

your GPS module has TX, PPS or FIX indicator LED and do they lit up or are flashing?

It has a PPS LED, a TXD LED, a RXD LED and a PWR LED. The PPS one is not blinking.

If none of this works do you have a recommended Pi Zero W-friendly GPS HAT for your timing setup?

form the form-factor i would expect that your module IS already RPi Zero friendly.

It has a PPS LED, a TXD LED, a RXD LED and a PWR LED. The PPS one is not blinking.

but TXD should blinking otherwise i would not expect data comming from the GPS module. to the RPi.

there is another option to connect the GPS module via USB. if you have a cable you may connect the GPS to your windows PC i think Putty can also manage COM ports - only to see if you get any serial output and at what baud rate.

I'm going to try to redo the entire installation and check the physical connections. Will return tomorrow.

ok, i hope you will have luck tomorrow.

i updated my script and set up the speed to 9600 baud in the /etc/default/gpsd file and i enabled chrony to also take internet servers as source (to get at least the time from internet in case GPS and PPS is not working).

Thanks for your perseverance! If I fail with this one I might order another module. I would REALLY like this to work as the ping times to the closest NTP servers in Sweden where I live is around 40 milliseconds.

i just saw pictures of the GPS modules like you have and it seems there are different versions. does your module has jumpers on the top to select the output mode A: USB-GNSS, B: Pi-GNSS, C: Pi-USB. if you have such a module i think the jumpers have to be in mode B. both jumpers upright in the middle position if i see it right on the pictures.
Screenshot from 2021-12-27 21-49-30

They have jumpers that sit like this:
image0

The case is 3D-printed.

oh, looks like your module is in mode A: USB-GNSS
so you will not getting serial output via GPIO

Ok! I'll shut it down and move the jumpers.

yes, move the jumpers one position to the right on your picture to mode B: Pi - GNSS.
hope your will get serial output immediately on gpsmon when you start up everything again.

I got the PPS LED BLINKING!

Please excuse the All Caps. :)

does it work now?
do you get something ao gpsmon and ppstest /dev/pps0... ?

Image confirming sucess (I hope):
Skärmbild (27)

I have a few more questions if you don't mind?

:D ok everything is allright now.
good that you showed me your picture, otherwise we would fail tomorrow as well.... ;)

yes, ask me if you have questions.

  1. Does the "PPS0" row at "Offset" say how many nanoseconds off the Pi:s system time is from UTC?

  2. Sort of ties into question 1: Does your solution/setup make sure I get UTC time on the Pi? And not GPS time which I understand is a couple of seconds off from UTC time.

  3. Since I've set a static IPv4 address on the Pi for my local home network could I now use that very address in how-to-set-custom-ntp-servers-in-recent-raspbian-stretch-systems
    to set the time on other Pi:s (my laptop has it's own GPS receiver for time syncing)?

  4. To make sure future me and others who find this thread understands the solution for this particular HAT: I needed to move the jumpers to position B --> clone the repository, make executable and run the shell script --> and run the watch -n1 chronyc sourcestats -v?

In comparison to the Waveshare guide this HAT is much cheaper than the NEO-M8T HAT. Somehow the timing HAT is much more expensive, by ~70 GBP.

One new issue when I ran ntpdate on another Pi on the same network:
pi@raspberrypi:~ $ sudo ntpdate 192.168.1.12 27 Dec 22:16:35 ntpdate[19720]: no server suitable for synchronization found pi@raspberrypi:~ $

Why is this?

  1. Does the "PPS0" row at "Offset" say how many nanoseconds off the Pi:s system time is from UTC?

it is the offset between the current system time and the received PPS0 signal as far as i understand.
use the commant chronyc tracking to see more detailed information about how accurate your system time is according to the reference source.

2. Sort of ties into question 1: Does your solution/setup make sure I get UTC time on the Pi? And not GPS time which I understand is a couple of seconds off from UTC time.

i think you mean the GPS time of the satellites are different over time, because they don't have/accumulate leap seconds. they are about 19 seconds up to now i think. your server uses and serves the correct UTC time not the GPS time. you can also see it in your chronyc output when you compare the offsets with the other internet time sources. if your system would be GPS time instead of UTC then the offset yourd be around 19 seconds off.

3. Since I've set a static IPv4 address on the Pi for my local home network could I now use that very address in [https://raspberrypi.stackexchange.com/questions/85670/how-to-set-custom-ntp-servers-in-recent-raspbian-stretch-systems](url)
   to set the time on other Pi:s (my laptop has it's own GPS receiver for time syncing)?

yes, put the ip address of your RPi Zero as NTP server to your other RPi's and Laptop and they sould be syncing thier time against your RPi Zero.
on windows you can use w32tm /stripchart /computer:192.168.1.4 (replace the ip with yours) to show the how off your windows time is compared to your RPi.
if your RPi has avahi running it should also be visibly by its ost name in your local network like "hostname.local" if you don't like using the ip address. (e.g. try ping zero.local from other coumputers in case your hostname is zero)

4. To make sure future me and others who find this thread understands the solution for this particular HAT: I needed to move the jumpers to position B --> clone the repository, make executable and run the shell script --> and run the `watch -n1 chronyc sourcestats -v`?

YES,

  1. the module must be in mode B: Pi - GNSS
    Screenshot from 2021-12-27 21-49-30
  2. clone the project
    git clone https://github.com/beta-tester/RPi-GPS-PPS-StratumOne
  3. and run the script
    bash install-gps-pps.sh
  4. adjust the PPS pin in the /boot/config.txt file from
    dtoverlay=pps-gpio,gpiopin=4 # /dev/pps0
    to
    dtoverlay=pps-gpio,gpiopin=18 # /dev/pps0
  5. is your GPS module running at another speed than 9600 baud, you have to adjust the speed in the /etc/default/gpsd file.
    the option -s <speed> has to be adjusted in the line:
    GPSD_OPTIONS="-n -r -b -s 9600"
  6. reboot your RPi
  7. check if chrony is getting its time synced against one of the PPS0, PSM0 or PST0 sources at least after few minutes after your GPS has a fix.

PS.: do you power your RPi and your GPS module separately from USB?
i think you only need to power one of both. the other will get its power over HAT connector.

PPS.: take a look to Stratum One servers to add a stratum one server near by you. here is am example of a Stratum one time server in sweden you can add it to your /etc/chrony/stratum1/20-ntp-servers.conf file:

# SP Technical Research Institute of Sweden, Stockholm Sweden 
server  ntp3.sptime.se  iburst  minpoll 4  maxpoll 4

and comment out the pool.ntp.org entry at the top of the file. the public community ntp servers are not always stratum one servers and less accurate.

One new issue when I ran ntpdate on another Pi on the same network: pi@raspberrypi:~ $ sudo ntpdate 192.168.1.12 27 Dec 22:16:35 ntpdate[19720]: no server suitable for synchronization found pi@raspberrypi:~ $

Why is this?

i don't know.
where is that ntpdate command coming from?

the ntpdate program is to be retired from this distribution.
...
ntpdate/stable 1:4.2.8p15+dfsg-1 armhf
client for setting system time from NTP servers (deprecated)

depending on how your system is syncing its time systemd-timesyncd, chrony, ntpd, (depending what is installed)... you have to use its own mechanism to check its status.

sudo systemctl status systemd-timedated
sudo systemctl status chrony
sudo systemctl status ntpd

sudo journalctl -xe -u systemd-timedated
sudo journalctl -xe -u chrony
sudo journalctl -xe -u ntpd

man systemd-timedated
man chronyd
man ntpd

there should be only one single service running on the system to sync the time.

systemd-timesyncd is the default at RasPi OS Bullseye & Buster.
systemd-timesyncd deactivates itself if chrony or ntpd is installed.

EDIT: a random and not too old link Command to sync time with NTP server in Linux

Results of suggested commands on the Pi I was trying to set the time on (not the same as the Pi Zero 2 W with the GPS):

> pi@raspberrypi:~ $ sudo systemctl status systemd-timedated
● systemd-timedated.service - Time & Date Service
     Loaded: loaded (/lib/systemd/system/systemd-timedated.service; static)
     Active: inactive (dead)
       Docs: man:systemd-timedated.service(8)
             man:localtime(5)
             man:org.freedesktop.timedate1(5)
pi@raspberrypi:~ $ sudo systemctl status chrony
Unit chrony.service could not be found.
pi@raspberrypi:~ $ sudo systemctl status ntpd
Unit ntpd.service could not be found.
pi@raspberrypi:~ $ sudo journalctl -xe -u systemd-timedated
~
-- Journal begins at Sat 2000-01-01 00:24:31 UTC, ends at Mon 2021-12-27 23:59:49 UTC. --
-- No entries --
pi@raspberrypi:~ $ sudo journalctl -xe -u chrony
~
-- Journal begins at Sat 2000-01-01 00:24:31 UTC, ends at Tue 2021-12-28 00:00:25 UTC. --
-- No entries --
pi@raspberrypi:~ $ sudo journalctl -xe -u ntpd
~
-- Journal begins at Sat 2000-01-01 00:24:31 UTC, ends at Tue 2021-12-28 00:00:40 UTC. --
-- No entries --
pi@raspberrypi:~ $

By the looks of chronyc tracking I'm about 17 nanoseconds behind UTC time?

pi@raspberrypi:~ $ chronyc tracking
Reference ID : 50534D30 (PSM0)
Stratum : 1
Ref time (UTC) : Tue Dec 28 00:04:29 2021
System time : 0.000000017 seconds slow of NTP time
Last offset : -0.000000014 seconds
RMS offset : 0.000000034 seconds
Frequency : 4.179 ppm fast
Residual freq : -0.000 ppm
Skew : 0.001 ppm
Root delay : 0.000000001 seconds
Root dispersion : 0.000010140 seconds
Update interval : 8.0 seconds
Leap status : Normal
pi@raspberrypi:~ $

Results of suggested commands on the Pi I was trying to set the time on (not the same as the Pi Zero 2 W with the GPS)

that's normal. most of them will give you nothing, because it is not installed.
i thought you will see more from systemd-timedated.
was /etc/systemd/timesyncd.conf already modified to use your RPi Zero's time server?
what do you get with timedatectl

i realized, that i have also chrony installed (in client configuration) on my other RPi's i have.
(just installed the package and added my local time server and one external stratum one server from the internet)
so i can't remember what the above commands will show you normally.

By the looks of chronyc tracking I'm about 17 nanoseconds behind UTC time?

yes...
kind of from the point of view of the RPi...
at the time you was "looking" at tracking...
plus some extra errors on top e.g. (i am no expert):

  • RPi's GPIO Pin 18 and calculation delay,
  • 12ns jitter in the PPS line of the GNSS modules specs,
  • 30..60ns of the GNSS module system (specs)
  • position of the GNSS antenna (skyview, number of satellites in view, reflection, length of the cable)
  • environmantal distortions that a "cheap" GNSS module can not compensate.
  • environmantal distortions that also an expensiv high quality GNSS module can not compensate.

see also

oh, keep in mind, what you keep your time server + GNSS module at a constant temperature.
e.g. when i open my window (specially in the winter) i can cleearly see it when i plot the tracking pps graph.

sudo gnuplot ~/RPi-GPS-PPS-StratumOne/gnuplot/chrony-tracking.gnuplot
(you have to run it at the desktop)

i formated the link/url in your posts as they were put in a wrong format.
the correct format is:
[ <put the description to show here> ]( <put the url here> )
you put the url in the description part and the url part was nothing - so every time i clicked your url i got page not found.
best is you write the url, select the full url and click to "Add a link" or Ctrl+K, then the url is correctly in the round brackets, you only has to fill the description to show in the square brackets.

i closed the issue, but you still can add comments if you have questons (i hopefully can answer correctly)

leave a star ⭐, if you find my project useful ... 😃

EDIT: because you are also using Windows, this is maybe of interest for you:
Configuring Systems for High Accuracy

Results of suggested commands on the Pi I was trying to set the time on (not the same as the Pi Zero 2 W with the GPS)

that's normal. most of them will give you nothing, because it is not installed. i thought you will see more from systemd-timedated. was /etc/systemd/timesyncd.conf already modified to use your RPi Zero's time server? what do you get with timedatectl

i realized, that i have also chrony installed (in client configuration) on my other RPi's i have. (just installed the package and added my local time server and one external stratum one server from the internet) so i can't remember what the above commands will show you normally.

By the looks of chronyc tracking I'm about 17 nanoseconds behind UTC time?

yes... kind of from the point of view of the RPi... at the time you was "looking" at tracking... plus some extra errors on top e.g. (i am no expert):

  • RPi's GPIO Pin 18 and calculation delay,
  • 12ns jitter in the PPS line of the GNSS modules specs,
  • 30..60ns of the GNSS module system (specs)
  • position of the GNSS antenna (skyview, number of satellites in view, reflection, length of the cable)
  • environmantal distortions that a "cheap" GNSS module can not compensate.
  • environmantal distortions that also an expensiv high quality GNSS module can not compensate.

see also

oh, keep in mind, what you keep your time server + GNSS module at a constant temperature. e.g. when i open my window (specially in the winter) i can cleearly see it when i plot the tracking pps graph.

sudo gnuplot ~/RPi-GPS-PPS-StratumOne/gnuplot/chrony-tracking.gnuplot (you have to run it at the desktop)

On the Pi I tried to run ntpdate the /etc/systemd/timesyncd.conf file was modified so that the main server/servers was ntp.se (an anycast address for Sweden's official NTP service) and the fallback was left at the RPi OS default.

And the result of timedatectl on the Pi I want to set the time on:
Skärmbild (29)

How should I make it so that when other Pi:s query the Pi Zero 2 W they will get it and not some error message?

EDIT: Nevermind, I got the sudo ntpdate ip_address_of_server working now.
As long as the Pi Zero 2 W that is acting as the server is within 1 microsecond of UTC I'm very happy considering the price of the components.

which ntpdate packet did you installed - just the depricated ntpdate package or the newer ntpsec-ntpdate ?
both ntpdate versions have similar options.
-t for timeout,
-u direct mode for better chance to come through firewalls.
-b to force step the clock instead of slew.
sudo ntpdate -buv -t 5 192.168.1.12

EDIT: Nevermind, I got the sudo ntpdate ip_address_of_server working now. As long as the Pi Zero 2 W that is acting as the server is within 1 microsecond of UTC I'm very happy considering the price of the components.

now you can make an accurate happy new year count down clock ...
or an automated firework rocket launcher...
🕛 🎆 🎉 😄

if you install the package gpsd-client to the other computers, you also can use and visualize the GPS coorninates
e.g. xgps 192.168.1.12 for desktop or cgps 192.168.1.12 for console (where the ip of the RPi zero with the GPS must be)

Could I use a Python Library to "listen" on the GPIO pin used for PPS while it's disciplining the Pi's time and use the PPS to trigger some event like the tick of a counter?

My idea is detailed in this forum post of mine:
Using PPS Signal from GNSS Receiver to Trigger Camera

The intent as described in the post is to have the image interval be as precise as possible.

Alternatively could I have it so that the GNSS receiver sets the time as usual on the Pi4B but I use my regular code to capture the images and rely on the system clock being within +/- 1 millisecond of true time? If so the image captures would be within acceptable limits for me.

Or is there a possibility that I could use 2 GNSS receivers of which one sets the time for the Pi4B whilst the other one triggers the camera?
The second receiver would use another GPIO pin on the Pi4B for PPS and have Python "listen" on that pin which is not used for anything else.

My application is simply put a time lapse camera where the start of the capture sequence is precise to +/- 1 millisecond.

Suppose I want to capture a sequence of 3840x2160 PNG images every 60 seconds starting on 12:00:00 UTC and ending on 13:00:00 UTC.

That would translate to the first image being captured (i.e. the capture sequence/all steps to record the data for a single image) at 12:00:00 UTC and the final image being captured at 12:59:00 UTC for a total of 60 images.

I already have a working program that does this without any active disciplining of the system clock.
The critical part of the code is a while loop that as fast as the CPU will allow checks the system time and if it is equal to the time of the next image an image is captured, all within a window of time given by the user as say:

Start time (in UTC): 2021-12-01 00:00:00

Stop time (in UTC): 2021-12-02 00:00:00

Interval (in seconds, as an integer): 60

Using this info the total number of images is calculated and used to check if it's time to exit the while loop.

My thought was that by either having a GNSS receiver constantly disciplining the Pi4B whilst the program was running or having 2 receivers where one sets the time and one triggers the capture would aid in having the interval and time stamp of the images be as accurate as +/- 1 millisecond.

My understanding of how an image is captured tells me that only the start of the capture sequence needs to be known well.
As long as the image interval is longer than the steps the electronics goes through to capture and save the image data I don't mind that this image took x fractions of second to capture and save.

As for your "bouncing" time stamp I would suggest reducing the image resolution in pixels, I ran into this problem early on when I first got a Pi4B and using the picamera library. My time stamps were fluctuating like yours by about 1 second.

My hypothesis is that the buffer for the data gets saturated and needs more time to store the data but since the program wants to save new data before all of the previous data has been written to storage the interval necessarily goes up.

I set a minimum allowable interval of 11 seconds which really means that an integer value of 10 or lower will not be used and the program asks the user to enter another value.

So far I've written 3840x2160 PNG images of about 10 Mebibytes each at intervals of 20 seconds and not gotten the issue visible in your film.

My solution however causes the system time to drift somewhat over longer time lapse captures. That's why I need constant disciplining of the system time so I can trust the time stamps on the images.

Hi again,

How do I create a discussion thread on your repository so that this thread doesn't unnecessarily expand and I can ask you again in the future?

there is no "private message" available at github.
so best is to keep asking on this thread.
do not open new issues as long there is no new serious issue with my script.

Is there a way via Python to get the UTC offset for the system time like when I run that chronyc (or corresponding command)?

I plan to use a GNSS HAT and a SparkFun 20x4 Character LCD to make a UTC clock that along with the date and time displays the offset something like this:
2022-01-14 12:42:58 UTC Offset: -0.000000032 sec

i would try to see what i get:

  • run chronyc tracking inside python and parse the output for the required values. (gives the time offset of the local system).
  • enable logging in chronyd and watch to the offset values in the /var/log/chrony/tracking.log (gives the time offset of the local system).
  • run ppstest /dev/pps0 and parse the assert value the first part should be the seconds since 1970-01-01 00:00:00 and the value of interest is the ony behind the dot (only able on the system where GPS is attached).
  • run gpspipe -w <gpsd-server> and parse its PPS output clock_nsec. same as before but you can run it from any computer where you have a gpsd-client installed. (but it gives you the time offset of the time server where GPS is attached itself, not from the local system).

something like this:

import subprocess
...
with subprocess.Popen("sudo chronyc tracking", shell=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) as proc:
  out = proc.stdout
  error_code = proc.poll()
  if not error_code:
    str = out.readline().decode()
    index = str.find('System time     : ')
    ...

maybe run it in a loop in a separate thread.

New problem:
For some reason the PPS LED on the HAT I'm using has turned off or isn't blinking anymore. The physical connections all seem okay and the antenna has a clear view of the sky.

When I run the chronyc sourcestats (or similar) to see the offset from the different time sources on PPS0 it gives me an offset of ~+365 ms.

Could it be that I'm not getting a PPS signal through?

yes, that's possible. to me it happens also sometimes, specially when there are less satellites in view or most of them are lower than around 30° above horizont or there are interferences/noise from somewhere else.

the ~365ms are comming from the NMEA output over the serial port of your GPS.
if not already done, you can adjust the offset of the GPS0 to under +/- 200ms (see note2 in the readme)

you can also set a higher baud rate for the serial port (e.g. 115200), that sometimes improves the accuracy of the NMEA time codes.

and take a look to the manual of your GPS module if there is a setting where you can choose the mode/behavior when the PPS signal is provided. 3D FIX or if it is already good when there is 1D FIX - i can't remember how the option is called exactly.

i think where you live you will have not so many satellites straight above you. maybe you can enable the triple mode
GPS, GLONASS, Galileo - but then you definitely has to speed up your baud rate of your serial port, itherwise there are coming more date then the baud rate allowes.

but as i remember you told, that you removed the buffer battery from your GPS, so in case you change the PPS behavior, this and all other changed setting goes lost as soon your GPS looses its power.

if you have internet, add some stratum one time server close to your localtion to chrony.

to see if you computer gets PPS from the GPS, run sudo ppstest /dev/pps0

to see the sky view of your GPS, run xgps you should have at least 4 satellites in green

Which command do I use to add a stratum 1 server to chrony? I know of a few good ones in here in Sweden.

take a look to the file
/etc/chrony/stratum1/20-ntp-servers.conf
there are some stratum one servers for germany and belgium (commented out)
take this as template for yours and remove the # in front to get used. and comment out the poor pool ntp servers.

i would try to see what i get:

  • run chronyc tracking inside python and parse the output for the required values. (gives the time offset of the local system).

  • enable logging in chronyd and watch to the offset values in the /var/log/chrony/tracking.log (gives the time offset of the local system).

  • run ppstest /dev/pps0 and parse the assert value the first part should be the seconds since 1970-01-01 00:00:00 and the value of interest is the ony behind the dot (only able on the system where GPS is attached).

  • run gpspipe -w <gpsd-server> and parse its PPS output clock_nsec. same as before but you can run it from any computer where you have a gpsd-client installed. (but it gives you the time offset of the time server where GPS is attached itself, not from the local system).

something like this:

import subprocess

...

with subprocess.Popen("sudo chronyc tracking", shell=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) as proc:

  out = proc.stdout

  error_code = proc.poll()

  if not error_code:

    str = out.readline().decode()

    index = str.find('System time     : ')

    ...

maybe run it in a loop in a separate thread.

How to I run it in a separate thread in a loop in Python? I haven't done that before.

please read documentation:
https://docs.python.org/3/library/threading.html

maybe something like this.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#


import threading
import queue


stop_event = threading.Event()
data_queue = queue.Queue()


def data_collector_loop():
  try:
    while not stop_event.is_set():
      data_item = None

      # TODO: collect a data_item
      ...

      # put data_item to the data_queue to get handled by the other thread
      #   with timeout, to see if stop_event was fired
      if data_item:
        try:
          data_queue.put(data_item, timeout=2)
        except queue.Full as ex:
          print(f"Oops, lost data_item {data_item}, {ex}")
          continue

      # sleep 5s without missing stop_event
      stop_event.wait(timeout=5)
  except KeyboardInterrupt:
    pass
  except SystemExit:
    pass
  finally:
    stop_event.set()


def data_handler_loop():
  try:
    while not stop_event.is_set():
      data_item = None
      
      # wait for new data_items in data_queue
      #   with timeout, to see if stop_event was fired
      try:
        data_item = data_queue.get(timeout=2)
      except queue.Empty:
        print(f"Nothing to do.")
        continue

      # TODO: do something with the data_item
      ...
  except KeyboardInterrupt:
    pass
  except SystemExit:
    pass
  finally:
    stop_event.set()


def main(args):
  try:
    thread1 = threading.Thread(target=data_collector_loop, daemon=False)
    thread2 = threading.Thread(target=data_handler_loop, daemon=False)

    thread1.start()
    thread2.start()

    # TODO: do something else
    ...

    # block main thread until thread1 and thread2 are done
    thread1.join()
    thread2.join()
  except KeyboardInterrupt:
    pass
  except SystemExit:
    pass
  finally:
    stop_event.set()
  return 0


if __name__ == '__main__':
  import sys
  sys.exit(main(sys.argv))

New question:
Wouldn't Last offset be better than System time?

Or is System time with the seconds fast/slow of NTP time the same as Last offset, i.e. how many (fractions of a) second ahead/behind UTC with respect to what the GPS receiver is telling the Pi?

Would it be possible to just run it as a function and call the function inside the loop where I get the system time? This is my code so far:

from __future__ import print_function
import qwiic_serlcd
from time import sleep
from datetime import datetime, timezone
import subprocess

def get_offset():
    with subprocess.Popen("sudo chronyc tracking", shell=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) as proc:
        out = proc.stdout
        error_code = proc.poll()
        if not error_code:
            str = out.readline().decode()
            out = str[str.find("Last offset     : "):(str.find("Last offset     : ") + 12)] #Slices out the proper value from the str variable.
        return out

try:
    #Setup code for 20x4 LCD.
    lcd = qwiic_serlcd.QwiicSerlcd()

    lcd.setBacklight(255, 255, 255)
    lcd.setContrast(5)
    lcd.begin()
    lcd.disableSystemMessages()
    lcd.clearScreen()
    sleep(1)
    lcd.print("UTC Time:")
    lcd.setCursor(0, 2)
    lcd.print("Offset from UTC:")

    while True:
        lcd.setCursor(0, 1)
        lcd.print(datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S"))
        lcd.setCursor(0, 3)
        lcd.print(get_offset())
        sleep(0.1)

except KeyboardInterrupt:
    print("\nExited 'UTC_Clock.py'.")
    lcd.clearScreen()

My primary problem now is that I get a OSError: [Errno 121] Remote I/O error. According to SparkFun this is because the cable to the LCD isn't properly connected. This seems unlikely as I have checked several times now:
Troubleshooting Guide for LCD

i gave only an example.
you can take what ever you want and need.
i can't tell you what's best for you.
if you don't need to run other stuff in parallel, then you can do everything without threads.

Ok, though I'm having trouble slicing out the relevant part of the chronyc tracking.

Regarding the offset; is the System time output from chronyc tracking the preferred value if my intent is to obtain the offset from true UTC time?

Last offset from system time or UTC - where is the different.
if chrony tells the clock is 100ns off from the time source and chrony keeps the clock in sync, then the clock is 100ns off from system time and UTC in the same way... isn't it.

but please read the documentation of chrony and gpsd if you need the exact definition you are looking for.

Last offset from system time or UTC - where is the different. if chrony tells the clock is 100ns off from the time source and chrony keeps the clock in sync, then the clock is 100ns off from system time and UTC in the same way... isn't it.

but please read the documentation of chrony and gpsd if you need the exact definition you are looking for.

I checked the chrony documentation and it seems the "Last offset" section is the proper value.
So if my understanding of your answer and the documentation is correct the preferred value is Last offset then?

If I seem slow to understand slight differences I apologize for not making it clear enough.

I now have a (mostly) working program:

from __future__ import print_function
import qwiic_serlcd
from time import sleep
from datetime import datetime, timezone
import subprocess
import re

def get_offset():
    p = subprocess.Popen("sudo chronyc tracking | grep -i  Last ", stdout=subprocess.PIPE, shell=True)
    (output, err) = p.communicate()
    status = p.wait()
    offset_str = re.findall(r"[-+]?\d*\.\d+|\d+", str(output))[0]
    return offset_str

try:
    #Setup code for 20x4 LCD.
    lcd = qwiic_serlcd.QwiicSerlcd()

    lcd.setBacklight(255, 255, 255)
    lcd.setContrast(5)
    lcd.begin()
    lcd.disableSystemMessages()
    lcd.clearScreen()
    sleep(1)
    lcd.print("UTC Time:")
    lcd.setCursor(0, 2)
    lcd.print("Offset from UTC:")

    while True:
        lcd.setCursor(0, 1)
        lcd.print(datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S"))
        lcd.setCursor(0, 3)
        lcd.print(get_offset() + " seconds")
        sleep(0.2)

except BaseException:
    print("\nExited 'UTC_Clock.py'.")

finally:
    lcd.clearScreen()

My question is whether there is a faster way to access the Last offset value from Python than issuing a shell command and waiting for the output?

not that i know

Ok, thanks.

With the method used in my above posted code could I obtain the offset value of PPS0 from watch -n1 chronyc sourcestats -v?

And if not do you have any tips for how it could be done?

for other suggestions, please see my older comment:
#10 (comment)

i would try to see what i get:

  • run ppstest /dev/pps0 and parse the assert value the first part should be the seconds since 1970-01-01 00:00:00 and the value of interest is the ony behind the dot (only able on the system where GPS is attached).
  • run gpspipe -w <gpsd-server> and parse its PPS output clock_nsec. same as before but you can run it from any computer where you have a gpsd-client installed. (but it gives you the time offset of the time server where GPS is attached itself, not from the local system).

another way could be the statistics of PPS0 (/var/log/chrony/...), when logging and statistics of chrony are enabled (it should if /etc/chrony/stratum1/30-logging.conf is untouched).

I may come of as slow to understand but how do I get this to work?:

run gpspipe -w and parse its PPS output clock_nsec. same as before but you can run it from any computer where you have a gpsd-client installed. (but it gives you the time offset of the time server where GPS is attached itself, not from the local system).

I've tried these commands but get these errors:
pi@raspberrypi:~/Python_Code $ gpspipe -w clock_nsec gpspipe: could not connect to gpsd clock_nsec:2947, can't get host entry(-2) pi@raspberrypi:~/Python_Code $ sudo gpspipe -w clock_nsec gpspipe: could not connect to gpsd clock_nsec:2947, can't get host entry(-2) pi@raspberrypi:~/Python_Code $ sudo gpspipe -w <gpsd-server> -bash: syntax error near unexpected token 'newline'

I wan't to print the clock_nsec value to an LCD and if it as I understand it represents the system time offset in nanoseconds from UTC it would be the most desirable option. I don't need the offset displayed on another computer on the same network, just on the same Pi that is running the server and is attached to the GPS.

Based on the errors what am I doing wrong here?

please read the document !
https://gpsd.gitlab.io/gpsd/gpspipe.html

just execute gpspipe -w from the command line on the computer where gpsd is running and see what you get.
in case it is on another computer, then you have to give the name of the computer or ip like gpspipe -w my_gpsd_server or gpspipe -w 192.168.1.111

please read the document !

https://gpsd.gitlab.io/gpsd/gpspipe.html

just execute gpspipe -w from the command line on the computer where gpsd is running and see what you get.

in case it is on another computer, then you have to give the name of the computer or ip like gpspipe -w my_gpsd_server or gpspipe -w 192.168.1.111

Thanks! Sorry for the somewhat stupid question.