156746Sroberto/* 256746Sroberto * refclock_fg - clock driver for the Forum Graphic GPS datating station 356746Sroberto */ 456746Sroberto 556746Sroberto#ifdef HAVE_CONFIG_H 656746Sroberto# include <config.h> 756746Sroberto#endif 856746Sroberto 956746Sroberto#if defined(REFCLOCK) && defined(CLOCK_FG) 1056746Sroberto 1156746Sroberto#include "ntpd.h" 1256746Sroberto#include "ntp_io.h" 1356746Sroberto#include "ntp_refclock.h" 1456746Sroberto#include "ntp_calendar.h" 1556746Sroberto#include "ntp_stdlib.h" 1656746Sroberto 1756746Sroberto/* 1856746Sroberto * This driver supports the Forum Graphic GPS dating station. 1956746Sroberto * More information about FG GPS is available on http://www.forumgraphic.com 2056746Sroberto * Contact das@amt.ru for any question about this driver. 2156746Sroberto */ 2256746Sroberto 2356746Sroberto/* 2456746Sroberto * Interface definitions 2556746Sroberto */ 2656746Sroberto#define DEVICE "/dev/fgclock%d" 2756746Sroberto#define PRECISION (-10) /* precision assumed (about 1 ms) */ 2856746Sroberto#define REFID "GPS" 2956746Sroberto#define DESCRIPTION "Forum Graphic GPS dating station" 3056746Sroberto#define LENFG 26 /* timecode length */ 3156746Sroberto#define SPEED232 B9600 /* uart speed (9600 baud) */ 3256746Sroberto 3356746Sroberto/* 3456746Sroberto * Function prototypes 3556746Sroberto */ 36290001Sglebiusstatic int fg_init (int); 37290001Sglebiusstatic int fg_start (int, struct peer *); 38290001Sglebiusstatic void fg_shutdown (int, struct peer *); 39290001Sglebiusstatic void fg_poll (int, struct peer *); 40290001Sglebiusstatic void fg_receive (struct recvbuf *); 4156746Sroberto 4256746Sroberto/* 4356746Sroberto * Forum Graphic unit control structure 4456746Sroberto */ 4556746Sroberto 4656746Srobertostruct fgunit { 47290001Sglebius int pollnum; /* Use peer.poll instead? */ 48290001Sglebius int status; /* Hug to check status information on GPS */ 49290001Sglebius int y2kwarn; /* Y2K bug */ 5056746Sroberto}; 5156746Sroberto 5256746Sroberto/* 5356746Sroberto * Queries definition 5456746Sroberto */ 5556746Srobertostatic char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5656746Sroberto0, 0, 0, 0, 0, 0, 0, 0, 0 }; 5756746Srobertostatic char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5856746Sroberto0, 0, 0, 0, 0, 0, 0, 0, 0 }; 5956746Sroberto 6056746Sroberto/* 6156746Sroberto * Transfer vector 6256746Sroberto */ 6356746Srobertostruct refclock refclock_fg = { 64290001Sglebius fg_start, /* start up driver */ 65290001Sglebius fg_shutdown, /* shut down driver */ 66290001Sglebius fg_poll, /* transmit poll message */ 67290001Sglebius noentry, /* not used */ 68290001Sglebius noentry, /* initialize driver (not used) */ 69290001Sglebius noentry, /* not used */ 70290001Sglebius NOFLAGS /* not used */ 7156746Sroberto}; 7256746Sroberto 7356746Sroberto/* 7456746Sroberto * fg_init - Initialization of FG GPS. 7556746Sroberto */ 7656746Sroberto 7756746Srobertostatic int 7856746Srobertofg_init( 79290001Sglebius int fd 80290001Sglebius ) 8156746Sroberto{ 8256746Sroberto if (write(fd, fginit, LENFG) != LENFG) 83290001Sglebius return 0; 8456746Sroberto 85290001Sglebius return 1; 8656746Sroberto} 8756746Sroberto 8856746Sroberto/* 8956746Sroberto * fg_start - open the device and initialize data for processing 9056746Sroberto */ 9156746Srobertostatic int 9256746Srobertofg_start( 93290001Sglebius int unit, 9456746Sroberto struct peer *peer 9556746Sroberto ) 9656746Sroberto{ 9756746Sroberto struct refclockproc *pp; 9856746Sroberto struct fgunit *up; 9956746Sroberto int fd; 10056746Sroberto char device[20]; 10156746Sroberto 10256746Sroberto 10356746Sroberto /* 10456746Sroberto * Open device file for reading. 10556746Sroberto */ 106290001Sglebius snprintf(device, sizeof(device), DEVICE, unit); 10756746Sroberto 108290001Sglebius DPRINTF(1, ("starting FG with device %s\n",device)); 109290001Sglebius 110290001Sglebius fd = refclock_open(device, SPEED232, LDISC_CLK); 111290001Sglebius if (fd <= 0) 112290001Sglebius return (0); 11356746Sroberto 114290001Sglebius /* 115290001Sglebius * Allocate and initialize unit structure 116290001Sglebius */ 11756746Sroberto 118290001Sglebius up = emalloc(sizeof(struct fgunit)); 119290001Sglebius memset(up, 0, sizeof(struct fgunit)); 12056746Sroberto pp = peer->procptr; 121290001Sglebius pp->unitptr = up; 12256746Sroberto pp->io.clock_recv = fg_receive; 123290001Sglebius pp->io.srcclock = peer; 12456746Sroberto pp->io.datalen = 0; 12556746Sroberto pp->io.fd = fd; 12656746Sroberto if (!io_addclock(&pp->io)) { 127290001Sglebius close(fd); 128290001Sglebius pp->io.fd = -1; 129290001Sglebius return 0; 130290001Sglebius } 13156746Sroberto 13256746Sroberto 13356746Sroberto /* 13456746Sroberto * Initialize miscellaneous variables 13556746Sroberto */ 13656746Sroberto peer->precision = PRECISION; 13756746Sroberto pp->clockdesc = DESCRIPTION; 138290001Sglebius memcpy(&pp->refid, REFID, 3); 13956746Sroberto up->pollnum = 0; 14056746Sroberto 14156746Sroberto /* 14256746Sroberto * Setup dating station to use GPS receiver. 14356746Sroberto * GPS receiver should work before this operation. 144290001Sglebius */ 14556746Sroberto if(!fg_init(pp->io.fd)) 14656746Sroberto refclock_report(peer, CEVNT_FAULT); 14756746Sroberto 14856746Sroberto return (1); 14956746Sroberto} 15056746Sroberto 15156746Sroberto 15256746Sroberto/* 15356746Sroberto * fg_shutdown - shut down the clock 15456746Sroberto */ 15556746Srobertostatic void 15656746Srobertofg_shutdown( 15756746Sroberto int unit, 15856746Sroberto struct peer *peer 15956746Sroberto ) 16056746Sroberto{ 16156746Sroberto struct refclockproc *pp; 16256746Sroberto struct fgunit *up; 16356746Sroberto 16456746Sroberto pp = peer->procptr; 165290001Sglebius up = pp->unitptr; 166290001Sglebius if (pp->io.fd != -1) 167290001Sglebius io_closeclock(&pp->io); 168290001Sglebius if (up != NULL) 169290001Sglebius free(up); 17056746Sroberto} 17156746Sroberto 17256746Sroberto 17356746Sroberto/* 17456746Sroberto * fg_poll - called by the transmit procedure 17556746Sroberto */ 17656746Srobertostatic void 17756746Srobertofg_poll( 17856746Sroberto int unit, 17956746Sroberto struct peer *peer 18056746Sroberto ) 18156746Sroberto{ 18256746Sroberto struct refclockproc *pp; 18356746Sroberto 18456746Sroberto pp = peer->procptr; 18556746Sroberto 186290001Sglebius /* 187290001Sglebius * Time to poll the clock. The FG clock responds to a 188290001Sglebius * "<DLE>D<DLE><CR>" by returning a timecode in the format specified 189290001Sglebius * above. If nothing is heard from the clock for two polls, 190290001Sglebius * declare a timeout and keep going. 191290001Sglebius */ 19256746Sroberto 19356746Sroberto if (write(pp->io.fd, fgdate, LENFG) != LENFG) 194290001Sglebius refclock_report(peer, CEVNT_FAULT); 195290001Sglebius else 196290001Sglebius pp->polls++; 19756746Sroberto 19856746Sroberto /* 199290001Sglebius if (pp->coderecv == pp->codeproc) { 200290001Sglebius refclock_report(peer, CEVNT_TIMEOUT); 201290001Sglebius return; 202290001Sglebius } 20356746Sroberto */ 20456746Sroberto 205290001Sglebius record_clock_stats(&peer->srcadr, pp->a_lastcode); 20656746Sroberto 20756746Sroberto return; 20856746Sroberto 20956746Sroberto} 21056746Sroberto 21156746Sroberto/* 21256746Sroberto * fg_receive - receive data from the serial interface 21356746Sroberto */ 21456746Srobertostatic void 21556746Srobertofg_receive( 216290001Sglebius struct recvbuf *rbufp 217290001Sglebius ) 21856746Sroberto{ 219290001Sglebius struct refclockproc *pp; 22056746Sroberto struct fgunit *up; 221290001Sglebius struct peer *peer; 22256746Sroberto char *bpt; 22356746Sroberto 224290001Sglebius /* 225290001Sglebius * Initialize pointers and read the timecode and timestamp 22656746Sroberto * We can't use gtlin function because we need bynary data in buf */ 22756746Sroberto 228290001Sglebius peer = rbufp->recv_peer; 229290001Sglebius pp = peer->procptr; 230290001Sglebius up = pp->unitptr; 23156746Sroberto 23256746Sroberto /* 233290001Sglebius * Below hug to implement receiving of status information 234290001Sglebius */ 235290001Sglebius if(!up->pollnum) { 23656746Sroberto up->pollnum++; 23756746Sroberto return; 23856746Sroberto } 23956746Sroberto 24056746Sroberto 241290001Sglebius if (rbufp->recv_length < (LENFG - 2)) { 24256746Sroberto refclock_report(peer, CEVNT_BADREPLY); 243290001Sglebius return; /* The reply is invalid discard it. */ 24456746Sroberto } 24556746Sroberto 24656746Sroberto /* Below I trying to find a correct reply in buffer. 24756746Sroberto * Sometime GPS reply located in the beginnig of buffer, 24856746Sroberto * sometime you can find it with some offset. 24956746Sroberto */ 25056746Sroberto 25156746Sroberto bpt = (char *)rbufp->recv_space.X_recv_buffer; 252290001Sglebius while (*bpt != '\x10') 25356746Sroberto bpt++; 25456746Sroberto 25556746Sroberto#define BP2(x) ( bpt[x] & 15 ) 25656746Sroberto#define BP1(x) (( bpt[x] & 240 ) >> 4) 25756746Sroberto 258290001Sglebius pp->year = BP1(2) * 10 + BP2(2); 25956746Sroberto 260290001Sglebius if (pp->year == 94) { 26156746Sroberto refclock_report(peer, CEVNT_BADREPLY); 262290001Sglebius if (!fg_init(pp->io.fd)) 26356746Sroberto refclock_report(peer, CEVNT_FAULT); 264290001Sglebius return; 26556746Sroberto /* GPS is just powered up. The date is invalid - 26656746Sroberto discarding it. Initilize GPS one more time */ 26756746Sroberto /* Sorry - this driver will broken in 2094 ;) */ 26856746Sroberto } 26956746Sroberto 27056746Sroberto if (pp->year < 99) 271290001Sglebius pp->year += 100; 27256746Sroberto 273290001Sglebius pp->year += 1900; 274290001Sglebius pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4); 27556746Sroberto 27656746Sroberto/* 27756746Sroberto After Jan, 10 2000 Forum Graphic GPS receiver had a very strange 27856746Sroberto benahour. It doubles day number for an hours in replys after 10:10:10 UTC 27956746Sroberto and doubles min every hour at HH:10:ss for a minute. 28056746Sroberto Hope it is a problem of my unit only and not a Y2K problem of FG GPS. 28156746Sroberto Below small code to avoid such situation. 28256746Sroberto*/ 283290001Sglebius if (up->y2kwarn > 10) 284290001Sglebius pp->hour = BP1(6)*10 + BP2(6); 28556746Sroberto else 286290001Sglebius pp->hour = BP1(5)*10 + BP2(5); 28756746Sroberto 288290001Sglebius if ((up->y2kwarn > 10) && (pp->hour == 10)) { 289290001Sglebius pp->minute = BP1(7)*10 + BP2(7); 290290001Sglebius pp->second = BP1(8)*10 + BP2(8); 291290001Sglebius pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000; 292290001Sglebius pp->nsec += BP1(10) * 1000; 29356746Sroberto } else { 294290001Sglebius pp->hour = BP1(5)*10 + BP2(5); 295290001Sglebius pp->minute = BP1(6)*10 + BP2(6); 296290001Sglebius pp->second = BP1(7)*10 + BP2(7); 297290001Sglebius pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000; 298290001Sglebius pp->nsec += BP1(9) * 1000; 29956746Sroberto } 300290001Sglebius 301290001Sglebius if ((pp->hour == 10) && (pp->minute == 10)) { 30256746Sroberto up->y2kwarn++; 30356746Sroberto } 30456746Sroberto 305290001Sglebius snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 306290001Sglebius "%d %d %d %d %d", pp->year, pp->day, pp->hour, 307290001Sglebius pp->minute, pp->second); 30856746Sroberto pp->lencode = strlen(pp->a_lastcode); 309290001Sglebius /*get_systime(&pp->lastrec);*/ 31056746Sroberto 31156746Sroberto#ifdef DEBUG 312290001Sglebius if (debug) 313290001Sglebius printf("fg: time is %04d/%03d %02d:%02d:%02d UTC\n", 314290001Sglebius pp->year, pp->day, pp->hour, pp->minute, pp->second); 31556746Sroberto#endif 316290001Sglebius pp->disp = (10e-6); 317132451Sroberto pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */ 31856746Sroberto /* pp->leap = LEAP_NOWARNING; */ 31956746Sroberto 320290001Sglebius /* 321290001Sglebius * Process the new sample in the median filter and determine the 322290001Sglebius * timecode timestamp. 323290001Sglebius */ 32456746Sroberto 325290001Sglebius if (!refclock_process(pp)) 326290001Sglebius refclock_report(peer, CEVNT_BADTIME); 327290001Sglebius pp->lastref = pp->lastrec; 32856746Sroberto refclock_receive(peer); 32956746Sroberto return; 33056746Sroberto} 33156746Sroberto 33256746Sroberto 33356746Sroberto#else 33456746Srobertoint refclock_fg_bs; 33556746Sroberto#endif /* REFCLOCK */ 336