/src/ntp-dev/ntpd/refclock_fg.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #define PRECISION (-10) /* precision assumed (about 1 ms) */ |
28 | 0 | #define REFID "GPS" |
29 | 0 | #define DESCRIPTION "Forum Graphic GPS dating station" |
30 | 0 | #define LENFG 26 /* timecode length */ |
31 | 0 | #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 | 0 | { |
82 | 0 | if (write(fd, fginit, LENFG) != LENFG) |
83 | 0 | return 0; |
84 | | |
85 | 0 | return 1; |
86 | 0 | } |
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 | 0 | { |
97 | 0 | struct refclockproc *pp; |
98 | 0 | struct fgunit *up; |
99 | 0 | int fd; |
100 | 0 | char device[20]; |
101 | | |
102 | | |
103 | | /* |
104 | | * Open device file for reading. |
105 | | */ |
106 | 0 | snprintf(device, sizeof(device), DEVICE, unit); |
107 | |
|
108 | 0 | DPRINTF(1, ("starting FG with device %s\n",device)); |
109 | |
|
110 | 0 | fd = refclock_open(device, SPEED232, LDISC_CLK); |
111 | 0 | if (fd <= 0) |
112 | 0 | return (0); |
113 | | |
114 | | /* |
115 | | * Allocate and initialize unit structure |
116 | | */ |
117 | | |
118 | 0 | up = emalloc(sizeof(struct fgunit)); |
119 | 0 | memset(up, 0, sizeof(struct fgunit)); |
120 | 0 | pp = peer->procptr; |
121 | 0 | pp->unitptr = up; |
122 | 0 | pp->io.clock_recv = fg_receive; |
123 | 0 | pp->io.srcclock = peer; |
124 | 0 | pp->io.datalen = 0; |
125 | 0 | pp->io.fd = fd; |
126 | 0 | if (!io_addclock(&pp->io)) { |
127 | 0 | close(fd); |
128 | 0 | pp->io.fd = -1; |
129 | 0 | return 0; |
130 | 0 | } |
131 | | |
132 | | |
133 | | /* |
134 | | * Initialize miscellaneous variables |
135 | | */ |
136 | 0 | peer->precision = PRECISION; |
137 | 0 | pp->clockdesc = DESCRIPTION; |
138 | 0 | memcpy(&pp->refid, REFID, 3); |
139 | 0 | up->pollnum = 0; |
140 | | |
141 | | /* |
142 | | * Setup dating station to use GPS receiver. |
143 | | * GPS receiver should work before this operation. |
144 | | */ |
145 | 0 | if(!fg_init(pp->io.fd)) |
146 | 0 | refclock_report(peer, CEVNT_FAULT); |
147 | |
|
148 | 0 | return (1); |
149 | 0 | } |
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 | 0 | { |
161 | 0 | struct refclockproc *pp; |
162 | 0 | struct fgunit *up; |
163 | | |
164 | 0 | pp = peer->procptr; |
165 | 0 | up = pp->unitptr; |
166 | 0 | if (pp->io.fd != -1) |
167 | 0 | io_closeclock(&pp->io); |
168 | 0 | if (up != NULL) |
169 | 0 | free(up); |
170 | 0 | } |
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 | 0 | { |
182 | 0 | struct refclockproc *pp; |
183 | | |
184 | 0 | 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 | 0 | if (write(pp->io.fd, fgdate, LENFG) != LENFG) |
194 | 0 | refclock_report(peer, CEVNT_FAULT); |
195 | 0 | else |
196 | 0 | pp->polls++; |
197 | | |
198 | | /* |
199 | | if (pp->coderecv == pp->codeproc) { |
200 | | refclock_report(peer, CEVNT_TIMEOUT); |
201 | | return; |
202 | | } |
203 | | */ |
204 | |
|
205 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
206 | | |
207 | 0 | return; |
208 | |
|
209 | 0 | } |
210 | | |
211 | | /* |
212 | | * fg_receive - receive data from the serial interface |
213 | | */ |
214 | | static void |
215 | | fg_receive( |
216 | | struct recvbuf *rbufp |
217 | | ) |
218 | 0 | { |
219 | 0 | struct refclockproc *pp; |
220 | 0 | struct fgunit *up; |
221 | 0 | struct peer *peer; |
222 | 0 | 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 | 0 | peer = rbufp->recv_peer; |
229 | 0 | pp = peer->procptr; |
230 | 0 | up = pp->unitptr; |
231 | | |
232 | | /* |
233 | | * Below hug to implement receiving of status information |
234 | | */ |
235 | 0 | if(!up->pollnum) { |
236 | 0 | up->pollnum++; |
237 | 0 | return; |
238 | 0 | } |
239 | | |
240 | | |
241 | 0 | if (rbufp->recv_length < (LENFG - 2)) { |
242 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
243 | 0 | return; /* The reply is invalid discard it. */ |
244 | 0 | } |
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 | 0 | bpt = (char *)rbufp->recv_space.X_recv_buffer; |
252 | 0 | while (*bpt != '\x10') |
253 | 0 | bpt++; |
254 | |
|
255 | 0 | #define BP2(x) ( bpt[x] & 15 ) |
256 | 0 | #define BP1(x) (( bpt[x] & 240 ) >> 4) |
257 | | |
258 | 0 | pp->year = BP1(2) * 10 + BP2(2); |
259 | | |
260 | 0 | if (pp->year == 94) { |
261 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
262 | 0 | if (!fg_init(pp->io.fd)) |
263 | 0 | refclock_report(peer, CEVNT_FAULT); |
264 | 0 | 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 | 0 | } |
269 | | |
270 | 0 | if (pp->year < 99) |
271 | 0 | pp->year += 100; |
272 | |
|
273 | 0 | pp->year += 1900; |
274 | 0 | 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 | 0 | if (up->y2kwarn > 10) |
284 | 0 | pp->hour = BP1(6)*10 + BP2(6); |
285 | 0 | else |
286 | 0 | pp->hour = BP1(5)*10 + BP2(5); |
287 | |
|
288 | 0 | if ((up->y2kwarn > 10) && (pp->hour == 10)) { |
289 | 0 | pp->minute = BP1(7)*10 + BP2(7); |
290 | 0 | pp->second = BP1(8)*10 + BP2(8); |
291 | 0 | pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000; |
292 | 0 | pp->nsec += BP1(10) * 1000; |
293 | 0 | } else { |
294 | 0 | pp->hour = BP1(5)*10 + BP2(5); |
295 | 0 | pp->minute = BP1(6)*10 + BP2(6); |
296 | 0 | pp->second = BP1(7)*10 + BP2(7); |
297 | 0 | pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000; |
298 | 0 | pp->nsec += BP1(9) * 1000; |
299 | 0 | } |
300 | |
|
301 | 0 | if ((pp->hour == 10) && (pp->minute == 10)) { |
302 | 0 | up->y2kwarn++; |
303 | 0 | } |
304 | |
|
305 | 0 | snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), |
306 | 0 | "%d %d %d %d %d", pp->year, pp->day, pp->hour, |
307 | 0 | pp->minute, pp->second); |
308 | 0 | pp->lencode = strlen(pp->a_lastcode); |
309 | | /*get_systime(&pp->lastrec);*/ |
310 | |
|
311 | 0 | #ifdef DEBUG |
312 | 0 | if (debug) |
313 | 0 | printf("fg: time is %04d/%03d %02d:%02d:%02d UTC\n", |
314 | 0 | pp->year, pp->day, pp->hour, pp->minute, pp->second); |
315 | 0 | #endif |
316 | 0 | pp->disp = (10e-6); |
317 | 0 | 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 | 0 | if (!refclock_process(pp)) |
326 | 0 | refclock_report(peer, CEVNT_BADTIME); |
327 | 0 | pp->lastref = pp->lastrec; |
328 | 0 | refclock_receive(peer); |
329 | 0 | return; |
330 | 0 | } |
331 | | |
332 | | |
333 | | #else |
334 | | int refclock_fg_bs; |
335 | | #endif /* REFCLOCK */ |