1 /* 2 * refclock_fg - clock driver for the Forum Graphic GPS datating station 3 */ 4 5 #ifdef HAVE_CONFIG_H 6 # include <config.h> 7 #endif 8 9 #if defined(REFCLOCK) && defined(CLOCK_FG) 10 11 #include "ntpd.h" 12 #include "ntp_io.h" 13 #include "ntp_refclock.h" 14 #include "ntp_calendar.h" 15 #include "ntp_stdlib.h" 16 17 /* 18 * This driver supports the Forum Graphic GPS dating station. 19 * More information about FG GPS is available on http://www.forumgraphic.com 20 * Contact das@amt.ru for any question about this driver. 21 */ 22 23 /* 24 * Interface definitions 25 */ 26 #define DEVICE "/dev/fgclock%d" 27 #define PRECISION (-10) /* precision assumed (about 1 ms) */ 28 #define REFID "GPS" 29 #define DESCRIPTION "Forum Graphic GPS dating station" 30 #define LENFG 26 /* timecode length */ 31 #define SPEED232 B9600 /* uart speed (9600 baud) */ 32 33 /* 34 * Function prototypes 35 */ 36 static int fg_init P((int)); 37 static int fg_start P((int, struct peer *)); 38 static void fg_shutdown P((int, struct peer *)); 39 static void fg_poll P((int, struct peer *)); 40 static void fg_receive P((struct recvbuf *)); 41 42 /* 43 * Forum Graphic unit control structure 44 */ 45 46 struct fgunit { 47 int pollnum; /* Use peer.poll instead? */ 48 int status; /* Hug to check status information on GPS */ 49 int y2kwarn; /* Y2K bug */ 50 }; 51 52 /* 53 * Queries definition 54 */ 55 static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 57 static char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 59 60 /* 61 * Transfer vector 62 */ 63 struct refclock refclock_fg = { 64 fg_start, /* start up driver */ 65 fg_shutdown, /* shut down driver */ 66 fg_poll, /* transmit poll message */ 67 noentry, /* not used */ 68 noentry, /* initialize driver (not used) */ 69 noentry, /* not used */ 70 NOFLAGS /* not used */ 71 }; 72 73 /* 74 * fg_init - Initialization of FG GPS. 75 */ 76 77 static int 78 fg_init( 79 int fd 80 ) 81 { 82 if (write(fd, fginit, LENFG) != LENFG) 83 return 0; 84 85 return (1); 86 87 } 88 89 /* 90 * fg_start - open the device and initialize data for processing 91 */ 92 static int 93 fg_start( 94 int unit, 95 struct peer *peer 96 ) 97 { 98 struct refclockproc *pp; 99 struct fgunit *up; 100 int fd; 101 char device[20]; 102 103 104 /* 105 * Open device file for reading. 106 */ 107 (void)sprintf(device, DEVICE, unit); 108 109 #ifdef DEBUG 110 if (debug) 111 printf ("starting FG with device %s\n",device); 112 #endif 113 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) 114 return (0); 115 116 /* 117 * Allocate and initialize unit structure 118 */ 119 120 if (!(up = (struct fgunit *) 121 emalloc(sizeof(struct fgunit)))) { 122 (void) close(fd); 123 return (0); 124 } 125 memset((char *)up, 0, sizeof(struct fgunit)); 126 pp = peer->procptr; 127 pp->unitptr = (caddr_t)up; 128 pp->io.clock_recv = fg_receive; 129 pp->io.srcclock = (caddr_t)peer; 130 pp->io.datalen = 0; 131 pp->io.fd = fd; 132 if (!io_addclock(&pp->io)) { 133 (void) close(fd); 134 return (0); 135 } 136 137 138 /* 139 * Initialize miscellaneous variables 140 */ 141 peer->precision = PRECISION; 142 pp->clockdesc = DESCRIPTION; 143 memcpy((char *)&pp->refid, REFID, 3); 144 up->pollnum = 0; 145 146 /* 147 * Setup dating station to use GPS receiver. 148 * GPS receiver should work before this operation. 149 */ 150 if(!fg_init(pp->io.fd)) 151 refclock_report(peer, CEVNT_FAULT); 152 153 return (1); 154 } 155 156 157 /* 158 * fg_shutdown - shut down the clock 159 */ 160 static void 161 fg_shutdown( 162 int unit, 163 struct peer *peer 164 ) 165 { 166 struct refclockproc *pp; 167 struct fgunit *up; 168 169 pp = peer->procptr; 170 up = (struct fgunit *)pp->unitptr; 171 io_closeclock(&pp->io); 172 free(up); 173 } 174 175 176 /* 177 * fg_poll - called by the transmit procedure 178 */ 179 static void 180 fg_poll( 181 int unit, 182 struct peer *peer 183 ) 184 { 185 struct refclockproc *pp; 186 187 pp = peer->procptr; 188 189 /* 190 * Time to poll the clock. The FG clock responds to a 191 * "<DLE>D<DLE><CR>" by returning a timecode in the format specified 192 * above. If nothing is heard from the clock for two polls, 193 * declare a timeout and keep going. 194 */ 195 196 if (write(pp->io.fd, fgdate, LENFG) != LENFG) 197 refclock_report(peer, CEVNT_FAULT); 198 else 199 pp->polls++; 200 201 if (peer->burst > 0) 202 return; 203 /* 204 if (pp->coderecv == pp->codeproc) { 205 refclock_report(peer, CEVNT_TIMEOUT); 206 return; 207 } 208 */ 209 peer->burst = NSTAGE; 210 211 record_clock_stats(&peer->srcadr, pp->a_lastcode); 212 213 214 return; 215 216 } 217 218 /* 219 * fg_receive - receive data from the serial interface 220 */ 221 static void 222 fg_receive( 223 struct recvbuf *rbufp 224 ) 225 { 226 struct refclockproc *pp; 227 struct fgunit *up; 228 struct peer *peer; 229 char *bpt; 230 231 /* 232 * Initialize pointers and read the timecode and timestamp 233 * We can't use gtlin function because we need bynary data in buf */ 234 235 peer = (struct peer *)rbufp->recv_srcclock; 236 pp = peer->procptr; 237 up = (struct fgunit *)pp->unitptr; 238 239 /* 240 * Below hug to implement receiving of status information 241 */ 242 if(!up->pollnum) 243 { 244 up->pollnum++; 245 return; 246 } 247 248 249 if (rbufp->recv_length < (LENFG-2)) 250 { 251 refclock_report(peer, CEVNT_BADREPLY); 252 return; /* The reply is invalid discard it. */ 253 } 254 255 /* Below I trying to find a correct reply in buffer. 256 * Sometime GPS reply located in the beginnig of buffer, 257 * sometime you can find it with some offset. 258 */ 259 260 bpt = (char *)rbufp->recv_space.X_recv_buffer; 261 while(*bpt != '') 262 bpt++; 263 264 #define BP2(x) ( bpt[x] & 15 ) 265 #define BP1(x) (( bpt[x] & 240 ) >> 4) 266 267 pp->year = BP1(2)*10 + BP2(2); 268 269 if(pp->year == 94) 270 { 271 refclock_report(peer, CEVNT_BADREPLY); 272 if(!fg_init(pp->io.fd)) 273 refclock_report(peer, CEVNT_FAULT); 274 return; 275 /* GPS is just powered up. The date is invalid - 276 discarding it. Initilize GPS one more time */ 277 /* Sorry - this driver will broken in 2094 ;) */ 278 } 279 280 if (pp->year < 99) 281 pp->year += 100; 282 283 pp->year += 1900; 284 pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4); 285 286 /* 287 After Jan, 10 2000 Forum Graphic GPS receiver had a very strange 288 benahour. It doubles day number for an hours in replys after 10:10:10 UTC 289 and doubles min every hour at HH:10:ss for a minute. 290 Hope it is a problem of my unit only and not a Y2K problem of FG GPS. 291 Below small code to avoid such situation. 292 */ 293 if(up->y2kwarn > 10) 294 pp->hour = BP1(6)*10 + BP2(6); 295 else 296 pp->hour = BP1(5)*10 + BP2(5); 297 298 if((up->y2kwarn > 10) && (pp->hour == 10)) 299 { 300 pp->minute = BP1(7)*10 + BP2(7); 301 pp->second = BP1(8)*10 + BP2(8); 302 pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000; 303 pp->nsec += BP1(10) * 1000; 304 } else { 305 pp->hour = BP1(5)*10 + BP2(5); 306 pp->minute = BP1(6)*10 + BP2(6); 307 pp->second = BP1(7)*10 + BP2(7); 308 pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000; 309 pp->nsec += BP1(9) * 1000; 310 } 311 312 if((pp->hour == 10) && (pp->minute == 10)) 313 { 314 up->y2kwarn++; 315 } 316 317 sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second); 318 pp->lencode = strlen(pp->a_lastcode); 319 /*get_systime(&pp->lastrec);*/ 320 321 #ifdef DEBUG 322 if (debug) 323 printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n", 324 pp->year, pp->day, pp->hour, pp->minute, pp->second); 325 #endif 326 pp->disp = (10e-6); 327 pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */ 328 /* pp->leap = LEAP_NOWARNING; */ 329 330 /* 331 * Process the new sample in the median filter and determine the 332 * timecode timestamp. 333 */ 334 335 if (!refclock_process(pp)) 336 refclock_report(peer, CEVNT_BADTIME); 337 pp->lastref = pp->lastrec; 338 refclock_receive(peer); 339 return; 340 } 341 342 343 #else 344 int refclock_fg_bs; 345 #endif /* REFCLOCK */ 346