Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ntp / ntpd / refclock_fg.c
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 != '\10')
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->msec = BP1(9)*10 + BP2(9);
303                 pp->usec = BP1(10);
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->msec = BP1(8)*10 + BP2(8);
309                 pp->usec = BP1(9);
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
327         if (peer->stratum <= 1)
328                 peer->refid = pp->refid;
329         pp->disp =  (10e-6);
330         pp->lastrec = rbufp->recv_time; /* Is it better then get_systime()? */
331         /* pp->leap = LEAP_NOWARNING; */
332
333         /*
334          * Process the new sample in the median filter and determine the
335          * timecode timestamp.
336          */
337
338         if (!refclock_process(pp))
339                 refclock_report(peer, CEVNT_BADTIME);
340         
341         refclock_receive(peer);
342         return;
343 }
344
345
346 #else
347 int refclock_fg_bs;
348 #endif /* REFCLOCK */