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 */ 3656746Srobertostatic int fg_init P((int)); 3756746Srobertostatic int fg_start P((int, struct peer *)); 3856746Srobertostatic void fg_shutdown P((int, struct peer *)); 3956746Srobertostatic void fg_poll P((int, struct peer *)); 4056746Srobertostatic void fg_receive P((struct recvbuf *)); 4156746Sroberto 4256746Sroberto/* 4356746Sroberto * Forum Graphic unit control structure 4456746Sroberto */ 4556746Sroberto 4656746Srobertostruct fgunit { 4756746Sroberto int pollnum; /* Use peer.poll instead? */ 4856746Sroberto int status; /* Hug to check status information on GPS */ 4956746Sroberto 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 = { 6456746Sroberto fg_start, /* start up driver */ 6556746Sroberto fg_shutdown, /* shut down driver */ 6656746Sroberto fg_poll, /* transmit poll message */ 6756746Sroberto noentry, /* not used */ 6856746Sroberto noentry, /* initialize driver (not used) */ 6956746Sroberto noentry, /* not used */ 7056746Sroberto NOFLAGS /* not used */ 7156746Sroberto}; 7256746Sroberto 7356746Sroberto/* 7456746Sroberto * fg_init - Initialization of FG GPS. 7556746Sroberto */ 7656746Sroberto 7756746Srobertostatic int 7856746Srobertofg_init( 7956746Sroberto int fd 8056746Sroberto ) 8156746Sroberto{ 8256746Sroberto if (write(fd, fginit, LENFG) != LENFG) 8356746Sroberto return 0; 8456746Sroberto 8556746Sroberto return (1); 8656746Sroberto 8756746Sroberto} 8856746Sroberto 8956746Sroberto/* 9056746Sroberto * fg_start - open the device and initialize data for processing 9156746Sroberto */ 9256746Srobertostatic int 9356746Srobertofg_start( 9456746Sroberto int unit, 9556746Sroberto struct peer *peer 9656746Sroberto ) 9756746Sroberto{ 9856746Sroberto struct refclockproc *pp; 9956746Sroberto struct fgunit *up; 10056746Sroberto int fd; 10156746Sroberto char device[20]; 10256746Sroberto 10356746Sroberto 10456746Sroberto /* 10556746Sroberto * Open device file for reading. 10656746Sroberto */ 10756746Sroberto (void)sprintf(device, DEVICE, unit); 10856746Sroberto 10956746Sroberto#ifdef DEBUG 11056746Sroberto if (debug) 11156746Sroberto printf ("starting FG with device %s\n",device); 11256746Sroberto#endif 11356746Sroberto if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) 11456746Sroberto return (0); 11556746Sroberto 11656746Sroberto /* 11756746Sroberto * Allocate and initialize unit structure 11856746Sroberto */ 11956746Sroberto 12056746Sroberto if (!(up = (struct fgunit *) 12156746Sroberto emalloc(sizeof(struct fgunit)))) { 12256746Sroberto (void) close(fd); 12356746Sroberto return (0); 12456746Sroberto } 12556746Sroberto memset((char *)up, 0, sizeof(struct fgunit)); 12656746Sroberto pp = peer->procptr; 12756746Sroberto pp->unitptr = (caddr_t)up; 12856746Sroberto pp->io.clock_recv = fg_receive; 12956746Sroberto pp->io.srcclock = (caddr_t)peer; 13056746Sroberto pp->io.datalen = 0; 13156746Sroberto pp->io.fd = fd; 13256746Sroberto if (!io_addclock(&pp->io)) { 13356746Sroberto (void) close(fd); 13456746Sroberto return (0); 13556746Sroberto } 13656746Sroberto 13756746Sroberto 13856746Sroberto /* 13956746Sroberto * Initialize miscellaneous variables 14056746Sroberto */ 14156746Sroberto peer->precision = PRECISION; 14256746Sroberto pp->clockdesc = DESCRIPTION; 14356746Sroberto memcpy((char *)&pp->refid, REFID, 3); 14456746Sroberto up->pollnum = 0; 14556746Sroberto 14656746Sroberto /* 14756746Sroberto * Setup dating station to use GPS receiver. 14856746Sroberto * GPS receiver should work before this operation. 14956746Sroberto */ 15056746Sroberto if(!fg_init(pp->io.fd)) 15156746Sroberto refclock_report(peer, CEVNT_FAULT); 15256746Sroberto 15356746Sroberto return (1); 15456746Sroberto} 15556746Sroberto 15656746Sroberto 15756746Sroberto/* 15856746Sroberto * fg_shutdown - shut down the clock 15956746Sroberto */ 16056746Srobertostatic void 16156746Srobertofg_shutdown( 16256746Sroberto int unit, 16356746Sroberto struct peer *peer 16456746Sroberto ) 16556746Sroberto{ 16656746Sroberto struct refclockproc *pp; 16756746Sroberto struct fgunit *up; 16856746Sroberto 16956746Sroberto pp = peer->procptr; 17056746Sroberto up = (struct fgunit *)pp->unitptr; 17156746Sroberto io_closeclock(&pp->io); 17256746Sroberto free(up); 17356746Sroberto} 17456746Sroberto 17556746Sroberto 17656746Sroberto/* 17756746Sroberto * fg_poll - called by the transmit procedure 17856746Sroberto */ 17956746Srobertostatic void 18056746Srobertofg_poll( 18156746Sroberto int unit, 18256746Sroberto struct peer *peer 18356746Sroberto ) 18456746Sroberto{ 18556746Sroberto struct refclockproc *pp; 18656746Sroberto 18756746Sroberto pp = peer->procptr; 18856746Sroberto 18956746Sroberto /* 19056746Sroberto * Time to poll the clock. The FG clock responds to a 19156746Sroberto * "<DLE>D<DLE><CR>" by returning a timecode in the format specified 19256746Sroberto * above. If nothing is heard from the clock for two polls, 19356746Sroberto * declare a timeout and keep going. 19456746Sroberto */ 19556746Sroberto 19656746Sroberto if (write(pp->io.fd, fgdate, LENFG) != LENFG) 19756746Sroberto refclock_report(peer, CEVNT_FAULT); 19856746Sroberto else 19956746Sroberto pp->polls++; 20056746Sroberto 20156746Sroberto if (peer->burst > 0) 20256746Sroberto return; 20356746Sroberto /* 20456746Sroberto if (pp->coderecv == pp->codeproc) { 20556746Sroberto refclock_report(peer, CEVNT_TIMEOUT); 20656746Sroberto return; 20756746Sroberto } 20856746Sroberto */ 20956746Sroberto peer->burst = NSTAGE; 21056746Sroberto 21156746Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 21256746Sroberto 21356746Sroberto 21456746Sroberto return; 21556746Sroberto 21656746Sroberto} 21756746Sroberto 21856746Sroberto/* 21956746Sroberto * fg_receive - receive data from the serial interface 22056746Sroberto */ 22156746Srobertostatic void 22256746Srobertofg_receive( 22356746Sroberto struct recvbuf *rbufp 22456746Sroberto ) 22556746Sroberto{ 22656746Sroberto struct refclockproc *pp; 22756746Sroberto struct fgunit *up; 22856746Sroberto struct peer *peer; 22956746Sroberto char *bpt; 23056746Sroberto 23156746Sroberto /* 23256746Sroberto * Initialize pointers and read the timecode and timestamp 23356746Sroberto * We can't use gtlin function because we need bynary data in buf */ 23456746Sroberto 23556746Sroberto peer = (struct peer *)rbufp->recv_srcclock; 23656746Sroberto pp = peer->procptr; 23756746Sroberto up = (struct fgunit *)pp->unitptr; 23856746Sroberto 23956746Sroberto /* 24056746Sroberto * Below hug to implement receiving of status information 24156746Sroberto */ 24256746Sroberto if(!up->pollnum) 24356746Sroberto { 24456746Sroberto up->pollnum++; 24556746Sroberto return; 24656746Sroberto } 24756746Sroberto 24856746Sroberto 24956746Sroberto if (rbufp->recv_length < (LENFG-2)) 25056746Sroberto { 25156746Sroberto refclock_report(peer, CEVNT_BADREPLY); 25256746Sroberto return; /* The reply is invalid discard it. */ 25356746Sroberto } 25456746Sroberto 25556746Sroberto /* Below I trying to find a correct reply in buffer. 25656746Sroberto * Sometime GPS reply located in the beginnig of buffer, 25756746Sroberto * sometime you can find it with some offset. 25856746Sroberto */ 25956746Sroberto 26056746Sroberto bpt = (char *)rbufp->recv_space.X_recv_buffer; 26156746Sroberto while(*bpt != '') 26256746Sroberto bpt++; 26356746Sroberto 26456746Sroberto#define BP2(x) ( bpt[x] & 15 ) 26556746Sroberto#define BP1(x) (( bpt[x] & 240 ) >> 4) 26656746Sroberto 26756746Sroberto pp->year = BP1(2)*10 + BP2(2); 26856746Sroberto 26956746Sroberto if(pp->year == 94) 27056746Sroberto { 27156746Sroberto refclock_report(peer, CEVNT_BADREPLY); 27256746Sroberto if(!fg_init(pp->io.fd)) 27356746Sroberto refclock_report(peer, CEVNT_FAULT); 27456746Sroberto return; 27556746Sroberto /* GPS is just powered up. The date is invalid - 27656746Sroberto discarding it. Initilize GPS one more time */ 27756746Sroberto /* Sorry - this driver will broken in 2094 ;) */ 27856746Sroberto } 27956746Sroberto 28056746Sroberto if (pp->year < 99) 28156746Sroberto pp->year += 100; 28256746Sroberto 28356746Sroberto pp->year += 1900; 28456746Sroberto pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4); 28556746Sroberto 28656746Sroberto/* 28756746Sroberto After Jan, 10 2000 Forum Graphic GPS receiver had a very strange 28856746Sroberto benahour. It doubles day number for an hours in replys after 10:10:10 UTC 28956746Sroberto and doubles min every hour at HH:10:ss for a minute. 29056746Sroberto Hope it is a problem of my unit only and not a Y2K problem of FG GPS. 29156746Sroberto Below small code to avoid such situation. 29256746Sroberto*/ 29356746Sroberto if(up->y2kwarn > 10) 29456746Sroberto pp->hour = BP1(6)*10 + BP2(6); 29556746Sroberto else 29656746Sroberto pp->hour = BP1(5)*10 + BP2(5); 29756746Sroberto 29856746Sroberto if((up->y2kwarn > 10) && (pp->hour == 10)) 29956746Sroberto { 30056746Sroberto pp->minute = BP1(7)*10 + BP2(7); 30156746Sroberto pp->second = BP1(8)*10 + BP2(8); 302132451Sroberto pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000; 303132451Sroberto pp->nsec += BP1(10) * 1000; 30456746Sroberto } else { 30556746Sroberto pp->hour = BP1(5)*10 + BP2(5); 30656746Sroberto pp->minute = BP1(6)*10 + BP2(6); 30756746Sroberto pp->second = BP1(7)*10 + BP2(7); 308132451Sroberto pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000; 309132451Sroberto pp->nsec += BP1(9) * 1000; 31056746Sroberto } 31156746Sroberto 31256746Sroberto if((pp->hour == 10) && (pp->minute == 10)) 31356746Sroberto { 31456746Sroberto up->y2kwarn++; 31556746Sroberto } 31656746Sroberto 31756746Sroberto sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second); 31856746Sroberto pp->lencode = strlen(pp->a_lastcode); 31956746Sroberto /*get_systime(&pp->lastrec);*/ 32056746Sroberto 32156746Sroberto#ifdef DEBUG 32256746Sroberto if (debug) 32356746Sroberto printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n", 32456746Sroberto pp->year, pp->day, pp->hour, pp->minute, pp->second); 32556746Sroberto#endif 32656746Sroberto pp->disp = (10e-6); 327132451Sroberto pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */ 32856746Sroberto /* pp->leap = LEAP_NOWARNING; */ 32956746Sroberto 33056746Sroberto /* 33156746Sroberto * Process the new sample in the median filter and determine the 33256746Sroberto * timecode timestamp. 33356746Sroberto */ 33456746Sroberto 33556746Sroberto if (!refclock_process(pp)) 33656746Sroberto refclock_report(peer, CEVNT_BADTIME); 337132451Sroberto pp->lastref = pp->lastrec; 33856746Sroberto refclock_receive(peer); 33956746Sroberto return; 34056746Sroberto} 34156746Sroberto 34256746Sroberto 34356746Sroberto#else 34456746Srobertoint refclock_fg_bs; 34556746Sroberto#endif /* REFCLOCK */ 346