Skip to content

lucaf/NTPSync

Repository files navigation

NTPSync

Simple NTP (Network Time Protocol) client — C library with a Python wrapper. Tested on Linux and OS X.

NTPSync runs a background thread that periodically queries an NTP server and keeps a monotonic, drift-corrected clock available to your application. It's designed for use cases where you need a precise, continuously-updated offset between your local clock and "true" NTP time — for example, synchronizing timestamps across distributed processes or measuring clock drift.

Features

  • Lightweight C core (NtpSync.c / NtpSync.h) with no external dependencies beyond standard sockets.
  • Background synchronization thread with a configurable re-sync interval.
  • Configurable maximum tolerated clock offset, with error reporting when synchronization accuracy is broken.
  • Optional error callback for handling send/receive/version/KOD ("kiss of death") failures.
  • Monotonic time accessor, independent of NTP corrections, for measuring elapsed time reliably.
  • Python bindings generated with SWIG (NtpSyncPy), so the C library can be driven directly from Python scripts.

Repository layout

File Description
NtpSync.h / NtpSync.c Core NTP client library.
UdpConn.h / UdpConn.c UDP socket helper used to talk to the NTP server.
ByteOrder.h Byte-order (endianness) helpers for parsing NTP packets.
DebugUtil.h / DebugUtil.c Debug/logging utilities.
NtpSyncPy.i SWIG interface file used to generate the Python wrapper.
NtpSyncPy.py Generated Python module (produced by SWIG).
NtpSyncPy_wrap.c Generated SWIG C wrapper code.
setup.py Python distutils/setup.py build script for the wrapper.
makeit.sh Convenience script to build the Python extension.
NtpSyncTest.py Example/test script showing how to use the Python API.

Requirements

  • A C compiler (gcc/clang) and standard build tools.
  • Python 2 with development headers (the Python wrapper and test script use Python 2 syntax, e.g. print statements).
  • SWIG to (re)generate the Python bindings.
  • NumPy — only needed to run NtpSyncTest.py, which uses it to compute basic statistics.

On Debian/Ubuntu-based systems you can install the prerequisites with:

sudo apt-get install build-essential swig python2 python2-dev python-pip
pip2 install numpy

Building

The makeit.sh script wraps the SWIG + setup.py build steps:

./makeit.sh

This will:

  1. Remove any previous build artifacts (_NtpSyncPy.so, build/, *.pyc).
  2. Run SWIG against NtpSyncPy.i to (re)generate the Python wrapper source.
  3. Run python setup.py build to compile the C extension.
  4. Copy the resulting _NtpSyncPy.so into the repository root, next to NtpSyncPy.py, so it can be imported directly.

To just clean the build artifacts without rebuilding:

./makeit.sh remove

Usage

C library

Include NtpSync.h and link against NtpSync.c (and its UdpConn.c / ByteOrder.h / DebugUtil.c dependencies). The public API is:

int    ntp_sync_start(char *ip_address, double max_offset_ms, int inter_sync_delay_ms);
void   ntp_sync_stop();
void   ntp_sync_set_time(double ms);
double ntp_sync_get_time();
double ntp_sync_start_time();
int    ntp_sync_error();
void   ntp_sync_on_error(tCbOnErr cb, void *prm);
double ntp_sync_monotonic_time();
Function Description
ntp_sync_start(ip_address, max_offset_ms, inter_sync_delay_ms) Starts the background sync thread against the given NTP server IP, tolerating up to max_offset_ms of error and re-syncing every inter_sync_delay_ms milliseconds. Returns 0 on success.
ntp_sync_stop() Stops the background sync thread.
ntp_sync_set_time(ms) Sets/resets the internal reference timestamp.
ntp_sync_get_time() Returns the current NTP-corrected time, in milliseconds.
ntp_sync_start_time() Returns the timestamp recorded when synchronization started.
ntp_sync_error() Returns the current error state (see eNtpSyncError), 0 meaning no error.
ntp_sync_on_error(cb, prm) Registers a callback invoked whenever a sync error occurs.
ntp_sync_monotonic_time() Returns a monotonic clock reading, unaffected by NTP corrections — useful for measuring elapsed time.

Possible error codes (eNtpSyncError):

  • eNtpSyncError_no — no error
  • eNtpSyncError_send — failed to send the NTP request
  • eNtpSyncError_receive — failed to receive the NTP response
  • eNtpSyncError_version — unexpected NTP protocol version
  • eNtpSyncError_kod — server sent a "kiss of death" response
  • eNtpSyncError_unexpected — unexpected/malformed response
  • eNtpSyncError_accuracy_broken — offset exceeded the configured tolerance

Python

After building the extension (./makeit.sh), import NtpSyncPy and call the same functions from Python:

import NtpSyncPy as nsp

# Start syncing against an NTP server, allowing up to 0.5ms offset,
# re-synchronizing every 5000ms.
nsp.ntp_sync_start("127.0.0.1", 0.5, 5000)

# Read the current NTP-corrected time and check for errors.
if nsp.ntp_sync_error():
    raise Exception("Synchronisation error (%d)" % nsp.ntp_sync_error())

current_time_ms = nsp.ntp_sync_get_time()

nsp.ntp_sync_stop()

Test script

NtpSyncTest.py is a small command-line tool that continuously syncs against an NTP server and prints delay statistics:

python2 NtpSyncTest.py --server 127.0.0.1 --offset 0.5 --synch-period 5000 --stats-period 5000

Options:

Flag Description Default
-a, --server NTP server IP address 127.0.0.1
-o, --offset Max clock offset tolerated, in ms 0.5
-s, --synch-period Interval between re-syncs, in ms 5000
-p, --stats-period Interval at which statistics are printed, in ms 5000

Author

Luca Filippin — luca.filippin@gmail.com

License

No license file is currently included in this repository. Please contact the author before reusing this code in your own projects.

About

Simple NTP (Network Time Protocol) client, C library + Python wrapper - (Linux, OS X)

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages