The Linus Tech Tips channel ShortCircuit recently did a video on an NTP time source card. Video here for reference:
It’s really nice to have your own clock source, but when you start digging into the price of those units, saying you might
not get change from $13,000 isn’t a lie. Of course, that’s for the highest end cards - but do we really need that for a
small user? What if you could get most of the way there for about $50 USD installed?
Good news - you can.
The idea is to use one of these GPS recievers with a little bit of level conversion
and hook it straight into your serial port. It’s exactly the same method as when I did this
about 8 years ago - however the tooling has changed a little since then - so lets revisit this topic with some modern tools.
We’ll be using chronyd as our NTP time server - as its pretty much the default everywhere these days, along with gpsd and its
tools to configure the module properly.
Looking at the GPS module, you’ll see it’s pretty straight forward. TX / RX for serial data, PPS for the pulse, and power.
Here’s a quick picture of my plug wiring. In my setup, my RS232 port allows me to inject 5vDC on pin 9 of the DB9. You probably
won’t have this, so you’ll need to supply 5vDC to the VCC pad on the converter - which also connects to the GPS Vcc - and one of the
GND pads on the board. It’s important that the serial port ground, the power supply ground, and the level converters grounds are
all connected.
On the hardware side, that’s pretty much it - so now, lets set up the software.
I installed mine on a Proxmox server - so everything here is based on a Debian install. These tools are generic, so search for them
on your distro.
Now, to configure gpsd, edit the file /etc/default/gpsd, and make its contents as below. Subsitute your serial port instead of /dev/ttyS0.
1
2
3
4
5
6
7
8
9
10
# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.
DEVICES=""
# Other options you want to pass to gpsd
GPSD_OPTIONS="-n"
OPTIONS="-s 38400 -F /run/gpsd.sock /dev/ttyS0"
# Automatically hot add/remove USB GPS devices via gpsdctl
USBAUTO="false"
Now we’re going to want to edit the gpsd systemd service to add some commands to initialise the GPS module on startup.
Do this via systemctl edit gpsd and add in the following - again, alter your serial port as required:
In this config, we deliberately add an error to the GPS lines. This is because the NMEA data can be quite regular, and in some cases
people have seen it being preferred as a source over the PPS. Inducing an error here will ensure that we always use the PPS source.
The last two lines allow your network to use the chronyd instance as an NTP source and sets an external reference to start against.
Now to configure these services to start on boot, and start them now:
If you’re like me, you’ve got a home lab with a dozen or so virtual machines doing all sorts of things - and each of them are pulling down updates from somewhere on the internet.
What if you could have a single endpoint for all VMs to reference? That way, updates that are common would be distributed to all systems at LAN speeds after the first download.
CacheEnable disk /fedora
CacheRoot /var/cache/httpd/fedora
CacheMaxFileSize 524288000
CacheDefaultExpire 14400
CacheDetailHeader on
# common caching directives
CacheQuickHandler off
CacheLock on
CacheLockPath /tmp/mod_cache-lock
CacheLockMaxAge 5
CacheHeader On
# cache control
#CacheIgnoreNoLastMod On
#CacheIgnoreCacheControl On
# unset headers from upstream server
Header unset Expires
Header unset Cache-Control
Header unset Pragma
ProxyRequests Off
ProxyPass /fedora http://dl.fedoraproject.org/pub/fedora
ProxyPassReverse /fedora http://dl.fedoraproject.org/pub/fedora
UseCanonicalName On
When in use, this will map http://my.apache.host/fedora to the Fedora mirror, and cache all responses and downloaded files.
The cache won’t automatically clean itself though - so we need a systemd service to clean things up over time. Create the file /etc/systemd/system/http-cache-clean.service as follows:
This will limit the cache size to 5Gb and remove the oldest files first.
There is one gotcha when using this with Fedoras updates and dnf - zchunk. I believe this is because mod_cache doesn’t work
on partial content requests - which is how zchunk functions.
To get around this, we can disable zchunk in the DNF configuration file /etc/dnf/dnf.conf. I also disable deltarpm - as its quicker to download the file from the LAN cache than it is
to rebuild a drpm update.
For years, I’ve run a set of Logitech Z-5500 speakers into an optical port on my PC. It gives good quality 5.1 audio, and supports AC3 + DTS digital passthrough as well as 44, 48, and 96khz bitrates.
The problem is, the speakers go into a ‘sleep’ mode where it takes nearly a second to bring the amp back online to play audio - so notification sounds are often not played at all.
To correct this, in the past, I’ve run a simple systemd service using sox to output a sine wave that is below the audible level like so: /usr/bin/play -q -n -c2 synth sin gain -95
Now however, we can do this directly within pipewire itself.
Firstly, we need to identify the output device using pw-top. Play some audio, and look for which sink it is being played on - eg:
1
2
3
4
5
6
7
8
9
10
11
12
13
S ID QUANT RATE WAIT BUSY W/Q B/Q ERR FORMAT NAME
S 28 0 0 --- --- --- --- 0 Dummy-Driver
S 29 0 0 --- --- --- --- 0 Freewheel-Driver
S 36 0 0 --- --- --- --- 0 Midi-Bridge
S 42 0 0 --- --- --- --- 0 alsa_output.usb-Kingston_HyperX_Cloud_Stinger_Core_Wireless___7.1_000000000000-00.analog-stereo
S 49 0 0 --- --- --- --- 0 alsa_input.usb-Kingston_HyperX_Cloud_Stinger_Core_Wireless___7.1_000000000000-00.mono-fallback
R 40 1024 48000 32.3us 4.3us 0.00 0.00 0 S16LE 2 48000 alsa_output.usb-Generic_USB_Audio-00.HiFi_5_1__hw_Audio_3__sink
R 106 1024 48000 20.5us 5.1us 0.00 0.00 0 F32LE 2 48000 + Brave
S 50 0 0 --- --- --- --- 0 alsa_output.usb-Generic_USB_Audio-00.HiFi_5_1__hw_Audio_1__sink
S 51 0 0 --- --- --- --- 0 alsa_output.usb-Generic_USB_Audio-00.HiFi_5_1__hw_Audio__sink
S 52 0 0 --- --- --- --- 0 alsa_input.usb-Generic_USB_Audio-00.HiFi_5_1__hw_Audio_2__source
S 53 0 0 --- --- --- --- 0 alsa_input.usb-Generic_USB_Audio-00.HiFi_5_1__hw_Audio_1__source
S 54 0 0 --- --- --- --- 0 alsa_output.pci-0000_2f_00.1.hdmi-stereo
In my case, the audio device is alsa_output.usb-Generic_USB_Audio-00.HiFi_5_1__hw_Audio_3__sink.
Now we create a file at ~/.config/wireplumber/main.lua.d/spdif-noise.lua with:
Now, when your first sound plays, pipewire will continue to output sub-audible noise to keep everything alive - which is a much better solution than using sox!
At this point, we could easily just cat out a file and treat email in folders as files and ignore the fact they were part of an imap mailbox. However, what happens if you
use something other than Maildir - like the newer mailbox formats? We can’t use the same approach, as each email
is likely not just a file anymore.
For example, dbox is Dovecot’s own high-performance mailbox format.
If we use mdbox, we can no longer open a single message per file, nor can we tell what folders are what from
the on disk layout. So we have to get smarter.
Using doveadm, we can search for messages in a mailbox, and fetch them to feed into our previously configured script and feed
them into PMG as before. The main advantage is that this will work with any mail storage backend.
This simple bash script will go through all users Spam or INBOX/Spam folders and fetch each one, feed it into the learning system, and then remove it from
the users mailbox.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bashMAILFILTER=my.pmg.install.example.com
shopt-s nullglob
doveadm search -A mailbox Spam OR mailbox INBOX/Spam | while read user guid uid;do
doveadm fetch -u$user text mailbox-guid $guid uid $uid | tail-n+2 > /tmp/spam.$guid.$uidcat /tmp/spam.$guid.$uid | ssh root@$MAILFILTER report
if[$?!= 0 ];then
echo"Error running sa-learn. Aborting."exit 1
fi
rm-f /tmp/spam.$guid.$uid
doveadm expunge -u$user mailbox-guid $guid uid $uiddone
Use it with the scripts / general configuration from the previous article, and this should be able to be used across all mail storage methods supported by Dovecot.
Cron it to run every 5 minutes or so, and you’re done! Nice and easy.