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