Thank you for
suggesting we use /sys/class/pps/pps1/assert to measure the clock. (Source code below.) Here's a comparison of the results between chrony and ntpd over 24 hours:
Other
processes may be affected by changing the CPU, so we haven't switched
to a constant frequency nor disabled the power saving states (e.g.
idle=poll).
Are there any other ways we could help chronyd stay below 20 microseconds (e.g., optimized build) that may have been overlooked (besides using GPIO)?
*ntpd had one 1500+ us outlier, which we removed from the graphs.
P.S.
Code improvement suggestions welcome:
// SOF
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#define PPS_DEVICE "/sys/class/pps/pps1/assert"
static void diff_timespec(
const struct timespec *time1,
const struct timespec *time0,
struct timespec* diff ) {
diff->tv_sec = time1->tv_sec - time0->tv_sec;
diff->tv_nsec = time1->tv_nsec - time0->tv_nsec;
if( diff->tv_nsec < 0 ) {
// nsec/sec
diff->tv_nsec += 1000000000;
diff->tv_sec--;
}
}
int main( int argc, char** argv ) {
struct timespec curr_pps_time = {0};
struct timespec prev_pps_time = {0};
int curr_assert = 0;
int prev_assert = -1;
struct timespec diff = {0};
struct timespec drift = {0};
char pps_buff[64] = {'\0'};
printf( "seconds,nanoSeconds\n" );
FILE* pps_fd = fopen( PPS_DEVICE, "r" );
while( 1 ) {
fread( pps_buff,1, 63, pps_fd );
sscanf( pps_buff, "%ld.%ld#%d",
&curr_pps_time.tv_sec,
&curr_pps_time.tv_nsec,
&curr_assert );
if( prev_assert == -1 ) {
prev_assert = curr_assert;
prev_pps_time.tv_sec = curr_pps_time.tv_sec;
prev_pps_time.tv_nsec = curr_pps_time.tv_nsec;
}
if( ( curr_assert - prev_assert ) == 1 ) {
struct timespec _one_second_ = {.tv_sec = 1, .tv_nsec = 0};
diff_timespec( &curr_pps_time, &prev_pps_time, &diff );
diff.tv_sec >= 1
? diff_timespec( &diff, &one_second, &drift )
: diff_timespec( &one_second, &diff, &drift );
printf( "%ld,%ld\n", drift.tv_sec, drift.tv_nsec );
fflush( stdout );
prev_assert = curr_assert;
prev_pps_time.tv_sec = curr_pps_time.tv_sec;
prev_pps_time.tv_nsec = curr_pps_time.tv_nsec;
}
fseek( pps_fd,0,SEEK_SET );
usleep( 250000 );
}
fclose( pps_fd );
return 0;
}
// EOF
Graphs were generated using R:
# SOF
plotOffsets <- function( filename ) {
csv.data <- read.table( filename, sep=",", header=T, na.strings="", stringsAsFactors=F );
csv.data$nanoSeconds <- csv.data$nanoSeconds / 1000;
plot(x=1:length(csv.data$nanoSeconds), csv.data$nanoSeconds, xlab="index", ylab="offset (us)", type="l");
}
# EOF