/* 20 jul 2003 pport hack v0.0 gnu@wraith.sf.ca.us v0.1 prune dead code 21 jul 2003 v0.2 "might be a bug" fix with PPCLRIRQ v0.3 revert fix--causes 40ms offset. 22 jul 2003 v0.4 flap pin 14 a la freebsd v0.5 fix poll argument v0.6 debug, syslog instrumentation 23 jul 2003 v0.7 getopt handling, merge parallel and serial versions v0.8 optionize flapping of pin 14 v0.9 multiple unit support 22 oct 2003 correct pport db25 ground pin (18 not 7) noticed by Glen Turner at aarnet */ /* usage: modprobe parport_pc io=0x378 irq=7 modprobe ppdev shm -d/dev/device -s (serial) or -p (parallel) -u (unit) (wire PPS to parallel pin 10, ground to 18, echo out to 14) */ /* SHM driver to allow PPS time sources to work without a PPS kernel */ /* The PPS pin must be connected to the DCD pin on a serial port */ /* This is Linux-specific code */ /* By: David J. Schwartz */ /* */ /* Version 1.03 */ /* Put into the public domain. Please keep the acknowledgement above */ /* intact. Portions taken from the NTP source (the time_shm structure */ /* and attach_shm code) are covered by the NTP copyright/license */ /* Compile with '-O2 -Os -static -march=[your_arch]' to minimize */ /* memory footprint and maximize accuracy. */ /* Notes: */ /* 1) You should have a line like the following in your /etc/ntpd.conf */ /* server 127.127.28.0 */ /* 2) Make sure no 'restrict' statement in your conf file prevents */ /* you from trusting the clock. If in doubt, add: */ /* restrict 127.127.28.0 */ /* 3) You must configure NTPD with the SHM clock driver */ /* TODO: */ /* 1) Average multiple PPS readings together, filtering out those that */ /* appear to be 'late'. */ /* 2) Have two states, synchronized and unsynchronized. Start in the */ /* unsynchronized state. If synchronized, go unsynchronized if most */ /* readings exceed the threshold. If unsynchronized, go synchronized */ /* if 'sysinfo' indicates a reasonable stratum and root distance. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Accept a timestamp if it's within this many microseconds of the */ /* system's second boundary -- do not exceed 250000 */ /* Setting this low helps avoid false lock, but requires a more accurate */ /* starting time synchronization. Setting this high makes capture easier */ /* but risks false lock if the clock starts too far off */ #define TOLERANCE 40000 /* Assume it takes this long for us to get scheduled and get the time */ /* (in microseconds) */ #define ASSUME_DELAY 0 /* Set this if a high-to-low transition marks the second boundary */ /* Normally, a low-to-high transition marks the second boundary */ /* #define HIGH_TO_LOW */ /* does nothing... use the -c (clear) flag instead */ /* Accuracy is assumed to be 2^PRECISION seconds -11 is approximately 490uS */ /* This is just an initial estimate, it will be adjusted */ #define PRECISION (-11) int nodive = 0; struct timeval tv; struct timezone tz; struct time_shm { int shm_mode; int stamp_count; time_t clock_sec; int clock_usec; time_t receive_sec; int receive_usec; int leap_indicator; int precision; int number_of_samples; int is_valid; int place_holder[10]; }; struct time_shm *sh; struct time_shm *attach_shm(int unit) { int shm_id=0; shm_id=shmget(0x4e545030+unit, sizeof(struct time_shm), IPC_CREAT|0700); if(shm_id==-1) { if (nodive) { fprintf(stderr,"shmget failed.\n"); perror("shmget"); exit(1); }; exit(1); }; sh=(struct time_shm *) shmat(shm_id, 0, 0); if(sh==(void *) -1 || sh==0) { if (nodive) { fprintf(stderr,"shmat failed.\n"); perror("shmat"); exit(1); }; exit(1); }; if (nodive) { fprintf(stderr,"shmat ok\n"); }; return sh; } /* This is our current estimate of precision. Our microseconds estimated */ /* error is 1000000*2^(precision_est/32) */ int precision_est=(PRECISION*32); int update_offset(int offset) { static int last_reading=0; static int average_offset=0; int target_precision; /* How far is this reading from the last one? */ int diff=(last_reading>offset) ? (last_reading-offset) : (offset-last_reading); /* On average, how much are these readings varying? */ /* In units of 16 microseconds, exponential average */ average_offset=(average_offset*15/16)+diff; last_reading=offset; if (nodive) { fprintf(stderr, "diff=%d\toff=%d\tavgdiff=%0.1f\n", diff, offset, average_offset/16.0); }; /* Adjust our precision estimate based upon how well our readings are */ /* agreeing with each other. This will give low values when our */ /* system clock's rate is off or due to system jitter, but that's */ /* actually reasonable. */ /* The magic constants are based on: */ /* average_offset/16/1000000=2^(target_precision/32) */ /* Or, to put it another way, our estimated precision is equal to our */ /* average offset of sequential readings after units are converted */ /* average_offset is in units of 16ths of a microsecond */ /* target_precision is in units of 32nds of a power of 2 */ if(average_offset<61) target_precision=-18*32; else if(average_offset<122) target_precision=-17*32; else if(average_offset<244) target_precision=-16*32; else if(average_offset<488) target_precision=-15*32; else if(average_offset<976) target_precision=-14*32; else if(average_offset<1953) target_precision=-13*32; else if(average_offset<3906) target_precision=-12*32; else if(average_offset<7812) target_precision=-11*32; else if(average_offset<15625) target_precision=-10*32; else if(average_offset<31250) target_precision=-9*32; else target_precision=-8*32; if(precision_esttarget_precision) precision_est--; /* gain precision slow */ if (nodive) { fprintf(stderr, "targ_prec=%.1f precision=%.1f\n", target_precision/32.0, precision_est/32.0); }; return average_offset; } void PutStamp(time_t cloc_sec, int cloc_usec, time_t sys_sec, int sys_usec) { sh->shm_mode=1; sh->leap_indicator=0; sh->is_valid=0; /* this is perhaps overly paranoid */ sh->stamp_count++; __asm__ __volatile__("": : :"memory"); sh->precision=precision_est/32; sh->clock_sec=cloc_sec; sh->clock_usec=cloc_usec; sh->receive_sec=sys_sec; sh->receive_usec=sys_usec; __asm__ __volatile__("": : :"memory"); sh->stamp_count++; sh->is_valid=1; if (nodive) { fprintf(stderr, "sys:%d/%d\tref:%d/%d\n", (int) sys_sec, (int) sys_usec, (int) cloc_sec, (int) cloc_usec); }; }; void frobtrans(void) { /* correct transition */ int fy; if(tv.tv_usec>(1000000-TOLERANCE)) { fy = update_offset(tv.tv_usec-1000000); PutStamp(tv.tv_sec+1, ASSUME_DELAY, tv.tv_sec, tv.tv_usec); } else if(tv.tv_usec