]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/ntpd/refclock_fg.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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         (int);
37 static  int     fg_start        (int, struct peer *);
38 static  void    fg_shutdown     (int, struct peer *);
39 static  void    fg_poll         (int, struct peer *);
40 static  void    fg_receive      (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  * fg_start - open the device and initialize data for processing
90  */
91 static int
92 fg_start(
93         int unit,
94         struct peer *peer
95         )
96 {
97         struct refclockproc *pp;
98         struct fgunit *up;
99         int fd;
100         char device[20];
101
102
103         /*
104          * Open device file for reading.
105          */
106         snprintf(device, sizeof(device), DEVICE, unit);
107
108         DPRINTF(1, ("starting FG with device %s\n",device));
109
110         fd = refclock_open(device, SPEED232, LDISC_CLK);
111         if (fd <= 0)
112                 return (0);
113         
114         /*
115          * Allocate and initialize unit structure
116          */
117
118         up = emalloc(sizeof(struct fgunit));
119         memset(up, 0, sizeof(struct fgunit));
120         pp = peer->procptr;
121         pp->unitptr = up;
122         pp->io.clock_recv = fg_receive;
123         pp->io.srcclock = peer;
124         pp->io.datalen = 0;
125         pp->io.fd = fd;
126         if (!io_addclock(&pp->io)) {
127                 close(fd);
128                 pp->io.fd = -1;
129                 return 0;
130         }
131
132         
133         /*
134          * Initialize miscellaneous variables
135          */
136         peer->precision = PRECISION;
137         pp->clockdesc = DESCRIPTION;
138         memcpy(&pp->refid, REFID, 3);
139         up->pollnum = 0;
140         
141         /* 
142          * Setup dating station to use GPS receiver.
143          * GPS receiver should work before this operation.
144          */
145         if(!fg_init(pp->io.fd))
146                 refclock_report(peer, CEVNT_FAULT);
147
148         return (1);
149 }
150
151
152 /*
153  * fg_shutdown - shut down the clock
154  */
155 static void
156 fg_shutdown(
157         int unit,
158         struct peer *peer
159         )
160 {
161         struct refclockproc *pp;
162         struct fgunit *up;
163         
164         pp = peer->procptr;
165         up = pp->unitptr;
166         if (pp->io.fd != -1)
167                 io_closeclock(&pp->io);
168         if (up != NULL)
169                 free(up);
170 }
171
172
173 /*
174  * fg_poll - called by the transmit procedure
175  */
176 static void
177 fg_poll(
178         int unit,
179         struct peer *peer
180         )
181 {
182         struct refclockproc *pp;
183         
184         pp = peer->procptr;
185
186         /*
187          * Time to poll the clock. The FG clock responds to a
188          * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
189          * above. If nothing is heard from the clock for two polls,
190          * declare a timeout and keep going.
191          */
192
193         if (write(pp->io.fd, fgdate, LENFG) != LENFG)
194                 refclock_report(peer, CEVNT_FAULT);
195         else
196                 pp->polls++;
197
198         /*
199         if (pp->coderecv == pp->codeproc) {
200                 refclock_report(peer, CEVNT_TIMEOUT);
201                 return;
202         }
203         */
204
205         record_clock_stats(&peer->srcadr, pp->a_lastcode);
206         
207         return;
208
209 }
210
211 /*
212  * fg_receive - receive data from the serial interface
213  */
214 static void
215 fg_receive(
216         struct recvbuf *rbufp
217         )
218 {
219         struct refclockproc *pp;
220         struct fgunit *up;
221         struct peer *peer;
222         char *bpt;
223
224         /*
225          * Initialize pointers and read the timecode and timestamp
226          * We can't use gtlin function because we need bynary data in buf */
227
228         peer = rbufp->recv_peer;
229         pp = peer->procptr;
230         up = pp->unitptr;
231
232         /*
233          * Below hug to implement receiving of status information
234          */
235         if(!up->pollnum) {
236                 up->pollnum++;
237                 return;
238         }
239
240         
241         if (rbufp->recv_length < (LENFG - 2)) {
242                 refclock_report(peer, CEVNT_BADREPLY);
243                 return; /* The reply is invalid discard it. */
244         }
245
246         /* Below I trying to find a correct reply in buffer.
247          * Sometime GPS reply located in the beginnig of buffer,
248          * sometime you can find it with some offset.
249          */
250
251         bpt = (char *)rbufp->recv_space.X_recv_buffer;
252         while (*bpt != '\x10')
253                 bpt++;
254
255 #define BP2(x) ( bpt[x] & 15 )
256 #define BP1(x) (( bpt[x] & 240 ) >> 4)
257         
258         pp->year = BP1(2) * 10 + BP2(2);
259         
260         if (pp->year == 94) {
261                 refclock_report(peer, CEVNT_BADREPLY);
262                 if (!fg_init(pp->io.fd))
263                         refclock_report(peer, CEVNT_FAULT);
264                 return;
265                  /* GPS is just powered up. The date is invalid -
266                  discarding it. Initilize GPS one more time */
267                 /* Sorry - this driver will broken in 2094 ;) */
268         }       
269         
270         if (pp->year < 99)
271                 pp->year += 100;
272
273         pp->year +=  1900;
274         pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
275
276 /*
277    After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
278    benahour. It doubles day number for an hours in replys after 10:10:10 UTC
279    and doubles min every hour at HH:10:ss for a minute.
280    Hope it is a problem of my unit only and not a Y2K problem of FG GPS. 
281    Below small code to avoid such situation.
282 */
283         if (up->y2kwarn > 10)
284                 pp->hour = BP1(6)*10 + BP2(6);
285         else
286                 pp->hour = BP1(5)*10 + BP2(5);
287
288         if ((up->y2kwarn > 10) && (pp->hour == 10)) {
289                 pp->minute = BP1(7)*10 + BP2(7);
290                 pp->second = BP1(8)*10 + BP2(8);
291                 pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
292                 pp->nsec += BP1(10) * 1000;
293         } else {
294                 pp->hour = BP1(5)*10 + BP2(5);
295                 pp->minute = BP1(6)*10 + BP2(6);
296                 pp->second = BP1(7)*10 + BP2(7);
297                 pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
298                 pp->nsec += BP1(9) * 1000;
299         }
300
301         if ((pp->hour == 10) && (pp->minute == 10)) {
302                 up->y2kwarn++;
303         }
304
305         snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
306                  "%d %d %d %d %d", pp->year, pp->day, pp->hour,
307                  pp->minute, pp->second);
308         pp->lencode = strlen(pp->a_lastcode);
309         /*get_systime(&pp->lastrec);*/
310
311 #ifdef DEBUG
312         if (debug)
313                 printf("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
314                        pp->year, pp->day, pp->hour, pp->minute, pp->second);
315 #endif
316         pp->disp =  (10e-6);
317         pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
318         /* pp->leap = LEAP_NOWARNING; */
319
320         /*
321          * Process the new sample in the median filter and determine the
322          * timecode timestamp.
323          */
324
325         if (!refclock_process(pp))
326                 refclock_report(peer, CEVNT_BADTIME);
327         pp->lastref = pp->lastrec;
328         refclock_receive(peer);
329         return;
330 }
331
332
333 #else
334 int refclock_fg_bs;
335 #endif /* REFCLOCK */