Raspberry Pi NTP Server - Part 6

Raspberry Pi NTP Server - Part 6

Optimising the Server

💡
Note: Due to server data loss, many images on this page are missing. The tests are being repeated to generate new images, but this will take some time. I'm using this as an opportunity to re-order the tests somewhat, so the page may read a bit strangely for a while.

If you've come this far, you've got a GNSS-backed Stratum 1 NTP server with excellent capability for experimentation, and the optimisation can begin!

One of the downsides of experimenting with NTP server optimisation is how long it takes for everything to settle after each change. It will generally take hours before you can see if your change has made a difference.

Hopefully this page can help save you some of your own time by letting you know which modification helped for me, and which didn't.

💡
Note: Many time-related terms will be used in this section. For a good top level explanation, check out the tracking command explanation in the Chrony docs.

Category Test Name Improvement Observed
Pi Stabilising the temperature Yes
Pi Finding Zero TC Yes
Pi Using a high-speed Micro SD Card Yes
Pi Returning to ntpheat In Progress
GNSS Setting the cable delay
GNSS Improving the antenna location
GNSS Using a timing antenna
GNSS Setting the power mode
GNSS Setting a fixed location
GNSS Disabling SBAS
GNSS Selecting GNSS constellations
GNSS Setting the PPS frequency
Pi Setting a fixed clock speed
Pi Disabling kernel power saving
Pi Disabling WiFi
Pi Disabling Energy Efficient Ethernet
Pi Decreasing the serial latency
Pi Changing the Power Supply
Chrony Setting the GNSS Offset
Chrony Changing to SOCK interface
Chrony Making Chrony stay in memory
Chrony Reducing 'niceness'
Chrony Using rtcsync
Chrony Adding Rate Limiting


Pi - Stabilising the temperature

Let's consider the performance of the server during a normal (almost) 24h period. The mean RMS offset over the day is a little under 1 µs, with spikes up to almost 4 µs.

Where does this come from? The answer becomes clear when we overlay the system frequency. Chrony is good at correcting for slow system drifts, but when the system frequency changes sharply the disturbance is passed along.

And why does the system frequency change? Let's now overlay the CPU temperature on the frequency. The system clock is generated by a crystal oscillator on the Raspberry Pi board, and these are sensitive to fluctuations in temperature. There are many ways that oscillator designers improve stability across temperature, but these come at a cost. Since this is a cheap single-board computer and not a piece of metrology equipment, low cost parts are used!

So if we want to improve the performance of our time server, we should start by looking at temperature.

Adding thermal mass

To slow down temperature changes (and hopefully therefore eliminate the sharp system frequency changes), I added a large metal heatsink to the Raspberry Pi. The heatsink doesn't directly contact the oscillator, but it's still going to provide a fairly large and stable thermal mass around it.

After 24 hours, the performance was as below. The system frequency is definitely showing fewer sharp spikes, and this in turn has improved the Mean RMS Offset. It's an reduction of a little over 25%, which isn't bad for just adding a big heatsink!

Adding insulation

Given the relationship between temperature stability and RMS Offset seen in the previous test, the Raspberry Pi was placed inside a small cardboard box with some bubble wrap. Not enough for it to overheat, but enough to give it a fairly stable air mass.

A day later, and the system performance was as shown in the image below. The temperature is more stable (though it's not so obvious given the 1 °C resolution of the sensor), and this has greatly smoothed the system frequency.

RMS offset has fallen significantly as a result - down to 227 ns, which is a reduction of over 65%.

Insulation clearly has a very significant effect. If your Pi isn't insulated against room temperature changes already, this is a great idea.

The temperature was still swinging around a bit as the room temperature changed over the course of the day, though. This brought me onto the ntpheat script, linked below.

Active temperature stabilisation

The program 'ntpheat' reduces CPU temperature variations by running calculations if the CPU temperature is below a certain setpoint, and stopping the calculations if above the setpoint. What an elegant solution!

RPi - ntpheat
Adding a simple program (ntpheat) to a Raspberry Pi to reduce the CPU temperature variations in a centrally heated environment.

The only change I had to make to the script linked above was to change the line:

        m.update("Nobody inspects the spammish repetition")     

So that it read:

        m.update(b'Nobody inspects the spammish repetition')

Move the script to /usr/bin/

sudo mv /home/pi/ntpheat.sh /usr/bin/

And make it executable

sudo chmod +x /usr/bin/ntpheat.sh 

Now we make a service:

sudo nano /etc/systemd/system/ntpheat.service

And paste in the following text.

Note, the -c modifier tells ntpheat how many times it should run. Running only once may not produce enough heat. The -t modifier tells it what setpoint to target.
[Unit]
Description=ntpheat

[Service]
ExecStart=python /usr/bin/ntpheat.sh -c 4 -t 50
Restart=on-failure

[Install]
WantedBy=multi-user.target

Now we enable the service, start it, and test to see that it's running.

sudo systemctl enable ntpheat.service
sudo systemctl start ntpheat.service
sudo systemctl status ntpheat.service

The image below shows how the system is performing after another 24 h period. The temperature is now very stable, as is the system frequency. It may not look like a huge change in system frequency stability, but look again at the Y axis - the variation is an order of magnitude less!

The mean RMS has dropped accordingly as a result of the better temperature stability. The 193 ns seen below is a reduction of another 15%.


Pi - Finding Zero TC

It's clear that temperature stability greatly helps the performance. We can never get perfect temperature stability, but we can move the setpoint to a temperature at which the crystal oscillator reacts less to temperature changes. This is the zero temperature coefficient (zero TC) point.

You can get a little more information and explanation about finding the zero TC point here:

How Much Heat?
Fair warning: if you are sick of reading how temperature affectsthe local frequency offset, then feel free click elsewhere. Forgood NTP performance you mere…

Let's edit the ntpheat service:

sudo nano /etc/systemd/system/ntpheat.service

Edit the line below:

ExecStart=python /usr/bin/ntpheat.sh -c 4 -t 50

The goal here is to sweep a range of temperatures until you find the point where the frequency offset reaches a min or max.

Once you set a temperature, save and close, then reload the daemon and restart ntpheat:

sudo systemctl daemon-reload 
sudo systemctl restart ntpheat.service

I did a rough sweep with 5 degree steps to find the approximate location of zero TC. You can see from the data below that the minimum occurs somewhere around 60 °C.

At this point, I decided to switch to 1 °C steps, but I didn't want to have to keep editing the ntpheat service over and over. Instead, I modified the ntpheat script (code below) and saved it as ntpheatsweep.sh

What this revised code does is to start warming to 5 °C below the requested point, then step up by 1 °C every two hours. Once it's stepped up ten times (so it's now 5 °C above the requested point), it starts to step back down. And so it repeats.

To use it, you pass the approximate temperature where you think zero TC is, and leave it to sweep back and forth.

#!/usr/bin/env python
#
# generate some heat!
#
# Wrap your RasPi in bubble wrap.  Then run ntpheat in the background.
# It will try to stabilize the CPU temperature at 65C by default.

# Sometimes one copy of ntpheat can use 100% of one CPU and
# still not heat up your RasPi as much as you want.  The temptation
# is to add more insulation to your RasPi, but then it will overshoot
# your target temperature if your load factor goes high.
#
# The solution is to run more than one copy of ntpheat.  This is
# easy to do with the -c option.
#
# To run 3 copies of ntpheat: ntpheat -c 3

import argparse
import hashlib
import os
import sys
import time

# Work with argvars
parser = argparse.ArgumentParser(description="make heat")
parser.add_argument('-c', '--copies',
                    default=[1],
                    dest='copies',
                    help="Number of copies to run.  Default is 1",
                    nargs=1,
                    type=int)
parser.add_argument('-t', '--temp',
                    default=[65.0],
                    dest='target_temp',
                    help="Temperature to hold.  Default is 65.0",
                    nargs=1,
                    type=float)
parser.add_argument('-w', '--wait',
                    default=[0.001],
                    dest='wait',
                    help="Set delay time in seconds, default is 0.1",
                    nargs=1,
                    type=float)
args = parser.parse_args()

args.copies[0] -= 1
while args.copies[0]:
    args.copies[0] -= 1
    pid = os.fork()
    if pid:
        # I am the fork
        break

zone0 = '/sys/class/thermal/thermal_zone0/temp'
cnt = 0

m = hashlib.md5()
temp = 0
max_cnt = args.wait[0] * 200000
# on a RasPi 3 the temp steps seem to be about 0.537 to 0.539C
temp_gate = args.target_temp[0]
starttime = time.time()

temp_setpoint = temp_gate - 5
step_no = 1
rising = True

while True:
    # on a RasPi 3, 200,000 of the m.update() can be one second
    delta = temp_setpoint - temp

    if 0 < delta:
        # heat it up
        m.update(b'Nobody inspects the spammish repetition')
    else:
        cnt = max_cnt
        # cools off slower than it heats up.
        # undocumented Python 'feature', no sleep less than 1 milli Sec
        sleep = args.wait[0] * 10.0 * -delta
        if 0.001 > sleep:
            sleep = 0.001
        time.sleep(sleep)

    cnt += 1
    # read the temperature every max_cnt
    if max_cnt < cnt:
        cnt = 0

        zone_data = open(zone0, 'r')
        for line in zone_data:
            temp = float(line) / 1000

        zone_data.close()

        currenttime = time.time()
        deltatime = currenttime - starttime

        if deltatime > 7200:

            if rising == True:
                temp_setpoint += 1
                step_no += 1
                # print(temp_setpoint)
            else:
                temp_setpoint -= 1
                step_no += 1
                # print(temp_setpoint)

            if step_no > 10:
                rising = not rising
                step_no = 1

            starttime = time.time()

I wanted this to run in the background just as ntpheat was, so I moved the new script to /usr/bin/ , made it executable, and edited the ntpheat service

sudo mv /home/pi/ntpheatsweep.sh /usr/bin/
sudo chmod +x /usr/bin/ntpheatsweep.sh 
sudo nano /etc/systemd/system/ntpheat.service

The ntpheat service was modified to comment out the path to ntpheat.sh, and add one to ntpheatsweep.sh :

[Unit]
Description=ntpheat

[Service]
#ExecStart=python /usr/bin/ntpheat.sh -c 4 -t 50
ExecStart=python /usr/bin/ntpheatsweep.sh -c 4 -t 60
Restart=on-failure

[Install]
WantedBy=multi-user.target

Now the daemon is reloaded, and the ntpheat service restarted

sudo systemctl daemon-reload
sudo systemctl restart ntpheat.service

Ultimately, the script didn't have to complete a full sweep - as shown in the image below, the zero TC turned out to be at 57 °C. If we then set ntpheat to this temperature, we'll minimise the effect of small temperature changes on the Pi's oscillator.

Note how the RMS offset spikes get smaller and smaller as we get closer to zero TC. This shows the very effect we're after!

The next step is to switch back to using ntpheat and setting it to our zero TC temperature.

sudo nano /etc/systemd/system/ntpheat.service

The ntpheat service was modified to comment out the path to ntpheatsweep.sh, and uncomment the one to ntpheat.sh. The setpoint was also changed to 57 °C.

[Unit]
Description=ntpheat

[Service]
ExecStart=python /usr/bin/ntpheat.sh -c 4 -t 57
#ExecStart=python /usr/bin/ntpheatsweep.sh -c 4 -t 60
Restart=on-failure

[Install]
WantedBy=multi-user.target

Now the daemon is reloaded, and the ntpheat service restarted

sudo systemctl daemon-reload
sudo systemctl restart ntpheat.service

Following 24 hours of operation at zero TC, the performance is as shown below. We see another good reduction in mean RMS offset. This now sits at 74 ns - a reduction of a further 62%!


Pi - Using a high-speed Micro SD Card

Does the Raspberry Pi's disk read/write performance affect timekeeping? It seems like a reasonable assumption - perhaps a higher maximum disk speed allows the system to operate away from any IO bottlenecks which may impact the periodicity. Alternatively, maybe a standard Micro SD card is already far from being a bottleneck.

Consider the table below (Source: Wikipedia), differentiating all the various speed classes of Micro SD cards can be a complex business. Class 10 can mean all manner of different write speeds!

The NTP server setup described so far has been created and operated on the left Micro SD card below. This is a generic low-cost Class 10 card. It could have a write speed of anywhere between 10 MB/s and 90 MB/s (but is probably at the low end).

The disk on the right is a new Micro SD card from Sandisk, which has more specific markings. Looking at the V30 class, we know this operates at 30 MB/s. Additionally, this has the newer Application Performance Class specified. This card is an A2 (the highest Application Performance Class), which means it can operate at 4,000 reading and 2,000 writing operations per second. This is a useful consideration for a card that applications (such as an operating system) will run on. (It was also under €10 - amazing what you can get for such a low price)

To perform this test, I used the Ubuntu 'Disks' application to make an image of the original Micro SD card, and wrote the image to the new Micro SD card. I then swapped the two cards over (so the Pi was running on the new one) and left it for 24 hours to tick along.

After a day, the performance was as shown below. The change did indeed seem to have some impact - the new value of mean RMS offset of 62.7 ns is a reduction of 15%.


💡
Below this point is old content which has yet to be updated

Pi - Returning to ntpheat

The previous experiment to find Zero TC got me wondering - is there an optimum number of copies of the script to run? (Recall that the number of copies is set using the -c modifier)

I used 4 copies in the previous tests, as in earlier experimentation I found that I needed more than one copy to reach a high enough temperature. That was as scientific as the process got. But perhaps there's a link between the number of copies an the stability, as they'll all be polling the temperature sensor and giving work to the CPU at slightly different times.

I modified the ntpheat script again, as below. The new script starts with one copy, and adds another every 24 hours until you stop it.

#!/usr/bin/env python
#
# generate some heat!
#
# Wrap your RasPi in bubble wrap.  Then run ntpheat in the background.
# It will try to stabilize the CPU temperature at 65C by default.

# Sometimes one copy of ntpheat can use 100% of one CPU and
# still not heat up your RasPi as much as you want.  The temptation
# is to add more insulation to your RasPi, but then it will overshoot
# your target temperature if your load factor goes high.
#
# The solution is to run more than one copy of ntpheat.  This is
# easy to do with the -c option.
#
# To run 3 copies of ntpheat: ntpheat -c 3

import argparse
import hashlib
import os
import sys
import time

# Work with argvars
parser = argparse.ArgumentParser(description="make heat")
parser.add_argument('-c', '--copies',
                    default=[1],
                    dest='copies',
                    help="Number of copies to run.  Default is 1",
                    nargs=1,
                    type=int)
parser.add_argument('-t', '--temp',
                    default=[65.0],
                    dest='target_temp',
                    help="Temperature to hold.  Default is 65.0",
                    nargs=1,
                    type=float)
parser.add_argument('-w', '--wait',
                    default=[0.001],
                    dest='wait',
                    help="Set delay time in seconds, default is 0.1",
                    nargs=1,
                    type=float)
args = parser.parse_args()


zone0 = '/sys/class/thermal/thermal_zone0/temp'
cnt = 0
fork = False

m = hashlib.md5()
temp = 0
max_cnt = args.wait[0] * 200000
# on a RasPi 3 the temp steps seem to be about 0.537 to 0.539C
temp_gate = args.target_temp[0]
starttime = time.time()


while True:
    # on a RasPi 3, 200,000 of the m.update() can be one second
    delta = temp_gate - temp

    if 0 < delta:
        # heat it up
        m.update(b'Nobody inspects the spammish repetition')
    else:
        cnt = max_cnt
        # cools off slower than it heats up.
        # undocumented Python 'feature', no sleep less than 1 milli Sec
        sleep = args.wait[0] * 10.0 * -delta
        if 0.001 > sleep:
            sleep = 0.001
        time.sleep(sleep)

    cnt += 1
    # read the temperature every max_cnt
    if max_cnt < cnt:
        cnt = 0

        zone_data = open(zone0, 'r')
        for line in zone_data:
            temp = float(line) / 1000

        zone_data.close()

        currenttime = time.time()
        deltatime = currenttime - starttime

        if deltatime > 86400:
            if fork == False:

                pid = os.fork()
                if pid:
                    # I am the fork
                    fork = True

                starttime = time.time()

The test was kicked off on a Saturday afternoon and left for a week. At the end of the test, the data was as below:


GNSS - Setting the cable delay

One of the GNSS receiver settings that really ought to have been set earlier is the cable delay. By telling the receiver how long the cable is, it'll give more precise PPS clock edges.

Because different coaxial cables have different delay characteristics, we need to do a quick calculation. Look at your cable and see what type it is, mine is RG174-U, then get a datasheet for that type.

I found a datasheet on Mouser that states that RG174-U has a delay of 5.03 ns/m, and my cable is 5 m long. So my cable delay is 25 ns.

Once you've worked out yours, enter it into the 'Cable Delay' field of the TP5 section in U-Center. Save the settings to the device (jump back to Part 4 if you need help saving the settings so they'll persist after restarts), and you should be good to go.

The results were pretty much as expected - the 50th percentile value didn't get any more stable, but the mean got closer to 0 ns.

Before

After


GNSS - Improving the antenna location

During the initial setup, the antenna was in a location that was easy to access, but had a poor sky view. Moving the antenna to an area with a much improved sky view gave a 50% increase in satellites used for the fix, a reduction in Time Dilution of Precision (TDOP), and an improvement in the clock offset as measured by GPSD.

Chrony itself showed less change. The issue is that the RMS Offset is spiky, and it doesn't settle fully between spikes. The spikes relate to points where the Raspberry Pi's oscillator frequency changed, which in turn relate to points where the temperature changed. he location with better sky view unfortunately has worse temperature stability. Temperature stability is of crucial importance in time and frequency metrology, and this result highlights it.

I think it's fair to say that an improved antenna location will improve performance, but if you've got enough satellites in view to get a fix, it's probably not the limiting factor in your system.

Before

After


GNSS - Using a timing antenna

Professional GNSS users will opt for proper GNSS timing antennas. These look at a narrower cone of the sky to reduce the effect of multipath interference and any malicious interference from ground level.

But will a professional timing antenna have any useful impact on our Raspberry Pi time server?

I borrowed one from work to find out.

After setting the system up with the timing antenna (NAIS CCAH32ST01) and leaving it to run for a day, the results were compared with a window of the same duration with the basic GNSS antenna.

The number of satellites seen reduced by around a third, and the Time Dilution of Precision (TDOP) was very slightly degraded. There was no visible improvement in the various percentile clock offsets or in the mean RMS Offset.

So, to revisit the question, did a professional timing antenna have any useful impact on our Raspberry Pi time server?

No.

(Which is good, as it removes the temptation to buy one of my own)

Timing antennas have their uses, but the Raspberry Pi NTP server (or more likely the GNSS receiver used in it) just isn't at the point where this short of investment makes a difference.

Before

After


GNSS - Setting the power mode

We know that the GNSS receiver is in its default configuration, as we reset it previously. Now the handy ubxtool command can be used to make changes.

The first thing to do is to check the version of your receiver.

ubxtool -p MON-VER ::/dev/ttyAMA0

In my case, I got the following response. Note the last line about protVer

UBX-MON-VER:
  swVersion ROM CORE 3.01 (107888)
  hwVersion 00080000
  extension FWVER=SPG 3.01
  extension PROTVER=18.00
  extension GPS;GLO;GAL;BDS
  extension SBAS;IMES;QZSS
WARNING:  protVer is 10.00, should be 18.00.  Hint: use option "-P 18.00"

Let's update the protVer and run the command again to see if the warning is gone.

export UBXOPTS="-P 18"
ubxtool -p MON-VER ::/dev/ttyAMA0

Looks like it's ready to go.

UBX-MON-VER:
  swVersion ROM CORE 3.01 (107888)
  hwVersion 00080000
  extension FWVER=SPG 3.01
  extension PROTVER=18.00
  extension GPS;GLO;GAL;BDS
  extension SBAS;IMES;QZSS

Now we check the power mode

ubxtool -p CFG-PMS ::/dev/ttyAMA0

And in response we see:

UBX-CFG-PMS:
 version 0 powerSetupValue 1 period 0 onTime 0x0 reserved1 0 0

A powerSetupValue of 1 means that the receiver is in 'Balanced' mode. Let's change this to 0 to enter into 'Full Power' mode

ubxtool -p CFG-PMS,0 ::/dev/ttyAMA0

A quick check that the setting change worked

ubxtool -p CFG-PMS ::/dev/ttyAMA0

The response we get is:

UBX-CFG-PMS:
 version 0 powerSetupValue 0 period 0 onTime 0x0 reserved1 0 0

Looks good, now we wait and see if there's an impact.

Start: 21-02 1700h

Note save via

ubxtool -p SAVE ::/dev/ttyAMA0


GNSS - Setting a fixed location

The u-blox GNSS receiver has twelve platform models available which adjust the navigation engine. According to the u-blox M8 Receiver Description, when correctly selected, these "improve the receiver's interpretation of the measurements and thus provide a more accurate position output".

Let's see what's set by default

ubxtool -p CFG-NAV5 ::/dev/ttyAMA0

The result is below:

UBX-CFG-NAV5:
 mask 0xffff dynModel 0 fixmode 3 fixedAlt 0 FixedAltVar 10000
 minElev 5 drLimit 0 pDop 250 tDop 250 pAcc 100 tAcc 350
 staticHoldThresh 0 dgpsTimeOut 60 cnoThreshNumSVs 0
 cnoThresh 0 res 0 staticHoldMaxDist 0 utcStandard 0
 reserved x0 0

dynModel is 0, which means 'Portable'. We can see from the u-blox M8 Receiver Description that 'Stationary' may be more suited to our use case.

Let's update the model:

ubxtool -p MODEL,2 ::/dev/ttyAMA0

And a sanity check that the setting worked

ubxtool -p CFG-NAV5 ::/dev/ttyAMA0

The output is as follows:

UBX-CFG-NAV5:
 mask 0xffff dynModel 2 fixmode 3 fixedAlt 0 FixedAltVar 10000
 minElev 5 drLimit 0 pDop 250 tDop 250 pAcc 100 tAcc 350
 staticHoldThresh 0 dgpsTimeOut 60 cnoThreshNumSVs 0
 cnoThresh 0 res 0 staticHoldMaxDist 0 utcStandard 0
 reserved x0 0

Now the usual waiting to see if it helps...

Start: 22-02 0900h

Note save via

ubxtool -p SAVE ::/dev/ttyAMA0


GNSS - Disabling SBAS

Section 19.2 of the u-blox M8 Receiver description states that "for best time pulse performance it is recommended to disable the SBAS subsystem."

Let's check out the currently enabled GNSS Constellations

ubxtool -p CFG-GNSS ::/dev/ttyAMA0

The output below shows that GPS, SBAS, QZSS, and GLONASS are enabled.

UBX-CFG-GNSS:
 msgVer 0  numTrkChHw 32 numTrkChUse 32 numConfigBlocks 7
  gnssId 0 TrkCh  8 maxTrCh 16 reserved 0 Flags x01010001
   GPS L1C/A enabled
  gnssId 1 TrkCh  1 maxTrCh  3 reserved 0 Flags x01010001
   SBAS L1C/A enabled
  gnssId 2 TrkCh  4 maxTrCh  8 reserved 0 Flags x01010000
   Galileo E1 
  gnssId 3 TrkCh  8 maxTrCh 16 reserved 0 Flags x01010000
   BeiDou B1I 
  gnssId 4 TrkCh  0 maxTrCh  8 reserved 0 Flags x03010000
   IMES L1 
  gnssId 5 TrkCh  0 maxTrCh  3 reserved 0 Flags x05010001
   QZSS L1C/A enabled
  gnssId 6 TrkCh  8 maxTrCh 14 reserved 0 Flags x01010001
   GLONASS L1 enabled

Now we disable SBAS

ubxtool -d SBAS ::/dev/ttyAMA0

An ACK comes back.

UBX-ACK-ACK:
  ACK to Class x06 (CFG) ID x3e (GNSS)

And a quick check to make sure that the setting worked.

ubxtool -p CFG-GNSS ::/dev/ttyAMA0

The result is as follows:

UBX-CFG-GNSS:
 msgVer 0  numTrkChHw 32 numTrkChUse 32 numConfigBlocks 7
  gnssId 0 TrkCh  8 maxTrCh 16 reserved 0 Flags x01010001
   GPS L1C/A enabled
  gnssId 1 TrkCh  1 maxTrCh  3 reserved 0 Flags x01010001
   SBAS L1C/A 
  gnssId 2 TrkCh  4 maxTrCh  8 reserved 0 Flags x01010000
   Galileo E1 
  gnssId 3 TrkCh  8 maxTrCh 16 reserved 0 Flags x01010000
   BeiDou B1I 
  gnssId 4 TrkCh  0 maxTrCh  8 reserved 0 Flags x03010000
   IMES L1 
  gnssId 5 TrkCh  0 maxTrCh  3 reserved 0 Flags x05010001
   QZSS L1C/A enabled
  gnssId 6 TrkCh  8 maxTrCh 14 reserved 0 Flags x01010001
   GLONASS L1 enabled

Note save & coldboot via

ubxtool -p SAVE ::/dev/ttyAMA0
ubxtool -p COLDBOOT ::/dev/ttyAMA0

So now we wait for the results.

Start 23-01 2100h


GNSS - Selecting GNSS constellations

The Galileo GNSS constellation provides very good timing performance due to the use of passive hydrogen masers on board the spacecraft.

Zhu et al found that "time transfer accuracy of BDS, GPS, GLONASS, and Galileo was 13.8 ns, 4.5 ns, 16.8 ns, and 4.2 ns, respectively" in the 2022 paper "GNSS Timing Performance Assessment and Results Analysis" Sensors 22, no. 7: 2486. https://doi.org/10.3390/s22072486

Our u-blox receiver doesn't enable Galileo by default, so let's enable it

ubxtool -e GALILEO ::/dev/ttyAMA0

An ACK comes back.

UBX-ACK-ACK:
  ACK to Class x06 (CFG) ID x3e (GNSS)

And a quick check to make sure that the setting worked.

ubxtool -p CFG-GNSS ::/dev/ttyAMA0

Galileo is enabled!

UBX-CFG-GNSS:
 msgVer 0  numTrkChHw 32 numTrkChUse 32 numConfigBlocks 7
  gnssId 0 TrkCh  8 maxTrCh 16 reserved 0 Flags x01010001
   GPS L1C/A enabled
  gnssId 1 TrkCh  1 maxTrCh  3 reserved 0 Flags x01010001
   SBAS L1C/A 
  gnssId 2 TrkCh  4 maxTrCh  8 reserved 0 Flags x01010001
   Galileo E1 enabled
  gnssId 3 TrkCh  8 maxTrCh 16 reserved 0 Flags x01010000
   BeiDou B1I 
  gnssId 4 TrkCh  0 maxTrCh  8 reserved 0 Flags x03010000
   IMES L1 
  gnssId 5 TrkCh  0 maxTrCh  3 reserved 0 Flags x05010001
   QZSS L1C/A enabled
  gnssId 6 TrkCh  8 maxTrCh 14 reserved 0 Flags x01010001
   GLONASS L1 enabled

Note save & coldboot via

ubxtool -p SAVE ::/dev/ttyAMA0
ubxtool -p COLDBOOT ::/dev/ttyAMA0

Now we can wait and see if there's an impact on timekeeping.

  • GPS & GLONASS
  • GPS
  • GLONASS
  • Galileo
  • Galileo & GPS
  • Galileo, GPS, GLONASS


GNSS - Setting the PPS frequency

Test planned.


Pi - Setting a fixed clock speed

sudo nano /boot/config.txt

# Run as fast as firmware / board allows
arm_boost=1
force_turbo=1

sudo reboot

Check with

sudo cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq

Start 01-02 2300h


Pi - Disabling kernel power saving

sudo nano /boot/config.txt

# Disable kernel power saving / dynamic ticks
nohz=off

sudo reboot

Start 03-02 1430h


Pi - Disabling WiFi

sudo nano /boot/config.txt

dtoverlay=pi3-disable-wifi

sudo reboot

Start 04-02 1230h


Pi - Disabling Energy Efficient Ethernet

Test planned.


Pi - Decreasing the serial latency

Test planned.


Pi - Changing the Power Supply

±2% Line reg!

Test planned.


Chrony - Setting the GNSS Offset

Test planned.


Chrony - Changing to SOCK interface

Test planned.


Chrony - Making Chrony stay in memory

Test planned.


Chrony - Reducing 'niceness'

Test planned.


Chrony - Using rtcsync

Test planned.


Chrony - Adding Rate Limiting

Test planned.

Also explore the impact of heavy load


Page Index