A command-line tool for synchronising your system clock from the NIST WWV shortwave time signal — no GPS, no NTP, no internet. Receives audio from any radio or sound card, detects the 1 kHz second-boundary ticks, and sets the clock to sub-second accuracy once it identifies the top of the minute.
skyclock decodes the NIST WWV time signal broadcast from Fort Collins, Colorado on 2.5, 5, 10, 15, and 20 MHz. It reads audio from a connected radio — through a PC sound card or directly from an audio file — and uses the signal to set the system clock. No GPS, no NTP, no internet required.
The primary use case is off-grid or emergency time synchronisation: when you have a shortwave receiver but no reliable internet connection, skyclock lets you set the system clock from the atomic clock in Colorado.
Optional hamlib integration lets skyclock control a connected transceiver — tune to the correct frequency, select AM mode — before starting the decode loop.
Tick-sync mode — working
The default mode detects the 1 kHz second-boundary tick that WWV
transmits at the start of every second. Once you provide the approximate
current UTC time (--time HH:MM:SS),
skyclock counts ticks to the next minute boundary and sets the clock to
that exact second — no BCD decode required, no consecutive clean minutes
needed. Works reliably even on noisy HF paths where the 100 Hz
subcarrier is unusable. Exits automatically after setting the clock.
Full BCD decode — temporarily broken
The --full-decode flag enables the
full time-code decoder, which reads BCD minute/hour/date directly from
the 100 Hz subcarrier without needing a user-supplied time.
This mode is currently undergoing rework and is not functional in
v0.3.0-BETA. Tick-sync mode is unaffected.
Requires FUSE2 on the host. If AppImages don't run on your distro:
sudo apt install libfuse2 or extract with
./skyclock*.AppImage --appimage-extract.
Requires Windows 10 or later (64-bit). No install — extract the zip to any
folder and run. Contains skyclock.exe
and all required runtime DLLs (PortAudio, MinGW).
Built for 64-bit Raspberry Pi OS (aarch64). Requires
libportaudio2 on the host.
Tested on Pi 4 and Pi 5.
Tick-sync is the default. Tune your radio to a WWV frequency (10 000 kHz is usually best in North America), set it to AM mode, and connect the audio output to your PC's microphone or line input. Then run:
skyclock will display a live signal bar and a countdown to the next minute boundary. When it detects the top of the minute, it sets the system clock to that second. The display shows the exact UTC timestamp even if you're running without root access — useful for verifying the signal before committing to a clock set.
Without root: the display shows Would set: HH:MM:SS.mmm UTC (run as root, or enable setSystemClock) so you can see exactly what would have been applied.
WWV transmits a one-minute frame of 60 bits, one bit per second. Each second begins with a 5 ms 1 kHz reference tick — the on-time point — followed immediately by a 100 Hz subcarrier burst whose duration encodes one BCD bit:
| Duration | Meaning |
|---|---|
| 200 ms | BCD zero |
| 500 ms | BCD one |
| 800 ms | Position marker — at seconds 0, 9, 19, 29, 39, and 49 of each frame |
The 60 BCD bits encode the current UTC minute, hour, day of year, year (last two digits), UT1 correction (±0.9 s), DST status, and a leap-second warning flag. Six position markers at fixed offsets within the frame provide the synchronisation points the decoder uses to find frame alignment.
The 1 kHz tick has substantially higher SNR than the 100 Hz subcarrier because it's a pure tone well above the audio noise floor and well outside the roll-off range of typical radio audio filters. Tick-sync mode exploits this asymmetry: detect the easy signal, ignore the hard one.
The decoder uses Goertzel filters —
single-frequency DFT evaluations — to measure signal power at exactly
1 kHz and 100 Hz every 10 ms. No FFT, no windowing,
no spectrogram. Several signal-processing techniques adapted from
D.L. Mills' NTP refclock_wwv
driver are layered to handle real-world HF propagation.
Tick detection with comb filter and median
A rising edge in the 1 kHz Goertzel filter exceeding the tracked noise floor is classified as an on-time tick. A 100-slot circular comb filter accumulates the 1 kHz EMA power at each 10 ms epoch within the second; the argmax of this buffer gives a stable, noise-averaged estimate of the tick's phase. A 3-sample running median of successive comb peaks smooths occasional outliers. An anti-spoof gate rejects false triggers caused by 100 Hz MARKER harmonics: genuine ticks arrive before the subcarrier starts, so the 100 Hz power is near floor at tick time; harmonics produce simultaneous elevated power at both frequencies. A 950 ms lockout prevents multipath echoes from re-triggering.
Free-running prediction with anchor correction
After the first real tick, subsequent second boundaries are predicted by advancing exactly 1 s per step. WWV's cesium standard is accurate to microseconds; audio clock drift is under 1 ms per minute at 50 ppm. When a prediction fires late (to allow a real tick a chance to appear first), the internal anchor is back-corrected by the same amount so sub-window boundaries remain aligned with the true second boundary rather than accumulating drift each miss.
Coherent energy integration
Rather than summing per-block 100 Hz power, the decoder accumulates raw audio samples into three fixed sub-windows after each tick (early 30–230 ms, mid 230–530 ms, late 530–830 ms) and runs a single coherent Goertzel over each full window. Coherent integration over 200–300 ms narrows the effective DFT bin from ±50 Hz (10 ms block) to ±2.5 Hz, reducing broadband noise contamination by ~20×. A separate 60 ms noise window (920–980 ms) tracks the 100 Hz noise floor as coherent energy E ≈ σ², which is used to subtract noise bias before computing per-window fractions. Under HF flutter fading, short incoherent signal fragments sum correctly into the right window without any single fragment needing to cross a threshold.
Q-channel phase EMA
A complex Goertzel over the early window yields both I and Q components at 100 Hz. The Q component tracks the subcarrier's instantaneous phase, which drifts slowly with Doppler shift on sky-wave paths. A slow EMA of Q accumulates this phase estimate across frames for potential future coherent combining.
Per-digit BCD accumulator
Each of the nine BCD digit fields (minute tens/units, hour tens/units, day hundreds/tens/units, year tens/units) maintains a 10-element EMA of per-value correlation scores. Each decoded frame boosts the winning digit's score and decays all others. A field "converges" once the same value leads for three consecutive frames. The number of converged fields (0–9) serves as a live lock-quality indicator.
SSB / I-Q demodulation
An optional frequency-shift demodulator mixes the input with a complex oscillator and two-stage IIR low-pass filters I and Q before feeding the in-phase channel to the Goertzel detectors. This lets skyclock receive WWV on a USB-mode receiver tuned slightly off-frequency — the oscillator shifts the 100 Hz subcarrier and 1 kHz tick back to their nominal baseband positions. Only the I channel is used (not the envelope); the Goertzel requires a real sinusoidal input, and the AM envelope of a baseband tone is constant DC.
skyclock is a self-contained C++17 application. On Linux (Debian/Ubuntu):
Cross-compile toolchain files for aarch64 Linux and Windows (MinGW-w64) are included in the repository.
Full BCD decode requires good audio
The --full-decode mode reads the
100 Hz subcarrier, which most radios heavily attenuate (AM audio
filters typically roll off sharply below 100–200 Hz). Without a
flat audio path down to 100 Hz, full decode is unreliable.
Tick-sync mode sidesteps this entirely since the 1 kHz tick is
always well within the passband.
Tick-sync requires a known approximate time
To identify which tick is the minute boundary, skyclock needs to know
the approximate UTC time to within ±29 seconds. Provide this with
--time HH:MM:SS. Without
--time, it falls back to the system
clock, which works if the system time is within ±29 s of UTC.
WWVH / overlapping transmissions
WWVH (Hawaii) transmits on the same frequencies. Both are receivable across much of North America and they overlap on some frequencies. Tuning to 5 MHz (WWV only) typically gives the cleanest single-station signal.
Signal processing techniques adapted from:
D.L. Mills, “WWV/H Reference Clock Driver”,
refclock_wwv.c, NTP Reference Implementation,
University of Delaware, Technical Report EE-97-8-1, 1997.
https://www.ntp.org/