2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
11 * Driver for some of the various the Motorola Oncore GPS receivers.
12 * should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12.
13 * The receivers with TRAIM (VP, UT, UT+), will be more accurate than the others.
14 * The receivers without position hold (GT, GT+) will be less accurate.
19 * COPYRIGHT 1991-1997 MOTOROLA INC. COPYRIGHT 1991-1996 MOTOROLA INC.
20 * SFTW P/N # 98-P36848P SFTW P/N # 98-P36830P
21 * SOFTWARE VER # 2 SOFTWARE VER # 8
22 * SOFTWARE REV # 2 SOFTWARE REV # 8
23 * SOFTWARE DATE APR 24 1998 SOFTWARE DATE 06 Aug 1996
24 * MODEL # R1121N1114 MODEL # B4121P1155
25 * HWDR P/N # 1 HDWR P/N # _
26 * SERIAL # R0010A SERIAL # SSG0226478
27 * MANUFACTUR DATE 6H07 MANUFACTUR DATE 7E02
31 * COPYRIGHT 1991-1996 MOTOROLA INC. COPYRIGHT 1991-2000 MOTOROLA INC.
32 * SFTW P/N # 98-P36830P SFTW P/N # 61-G10002A
33 * SOFTWARE VER # 8 SOFTWARE VER # 1
34 * SOFTWARE REV # 8 SOFTWARE REV # 3
35 * SOFTWARE DATE 06 Aug 1996 SOFTWARE DATE Mar 13 2000
36 * MODEL # B4121P1155 MODEL # P143T12NR1
37 * HDWR P/N # _ HWDR P/N # 1
38 * SERIAL # SSG0226478 SERIAL # P003UD
39 * MANUFACTUR DATE 7E02 MANUFACTUR DATE 0C27
42 * --------------------------------------------------------------------------
43 * This code uses the two devices
44 * /dev/oncore.serial.n
46 * which may be linked to the same device.
47 * and can read initialization data from the file
48 * /etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where
49 * n or N are the unit number, viz 127.127.30.N.
50 * --------------------------------------------------------------------------
51 * Reg.Clemens <reg@dwf.com> Sep98.
52 * Original code written for FreeBSD.
53 * With these mods it works on FreeBSD, SunOS, Solaris and Linux
54 * (SunOS 4.1.3 + ppsclock)
56 * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later).
58 * Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
59 * state machine state) are printed to CLOCKSTATS if that file is enabled
62 * --------------------------------------------------------------------------
64 * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
65 * doing an average of 10000 valid 2D and 3D fixes is what the automatic
66 * site survey mode does. Looking at the output from the receiver
67 * it seems like it is only using 3D fixes.
68 * When we do it ourselves, take 10000 3D fixes.
71 #define POS_HOLD_AVERAGE 10000 /* nb, 10000s ~= 2h45m */
74 * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
75 * "STATUS" line in the oncore config file, which contains the most recent
76 * copy of all types of messages we recognize. This file can be mmap(2)'ed
77 * by monitoring and statistics programs.
79 * See separate HTML documentation for this option.
86 #if defined(REFCLOCK) && defined(CLOCK_ONCORE) && defined(HAVE_PPSAPI)
90 #include "ntp_unixtime.h"
91 #include "ntp_refclock.h"
92 #include "ntp_stdlib.h"
97 #ifdef ONCORE_SHMEM_STATUS
98 # ifdef HAVE_SYS_MMAN_H
99 # include <sys/mman.h>
101 # define MAP_FAILED ((u_char *) -1)
102 # endif /* not MAP_FAILED */
103 # endif /* HAVE_SYS_MMAN_H */
104 #endif /* ONCORE_SHMEM_STATUS */
107 # ifdef HAVE_TIMEPPS_H
108 # include <timepps.h>
110 # ifdef HAVE_SYS_TIMEPPS_H
111 # include <sys/timepps.h>
116 #ifdef HAVE_SYS_SIO_H
117 # include <sys/sio.h>
120 #ifdef HAVE_SYS_TERMIOS_H
121 # include <sys/termios.h>
124 #ifdef HAVE_SYS_PPSCLOCK_H
125 # include <sys/ppsclock.h>
128 #ifndef HAVE_STRUCT_PPSCLOCKEV
130 # ifdef HAVE_STRUCT_TIMESPEC
137 #endif /* not HAVE_STRUCT_PPSCLOCKEV */
149 enum site_survey_state {
157 /* Model Name, derived from the @@Cj message.
158 * Used to initialize some variables.
174 /* the bits that describe these properties are in the same place
175 * on the VP/UT, but have moved on the M12. As such we extract
176 * them, and use them from this struct.
188 /* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to
189 * see what mode it is in. The bits on the M12 are multiplexed with
190 * other messages, so we have to 'keep' the last known mode here.
201 int unit; /* 127.127.30.unit */
202 struct refclockproc *pp;
205 int ttyfd; /* TTY file descriptor */
206 int ppsfd; /* PPS file descriptor */
207 int statusfd; /* Status shm descriptor */
212 enum receive_state o_state; /* Receive state */
213 enum posn_mode mode; /* 0D, 2D, 3D */
214 enum site_survey_state site_survey; /* Site Survey state */
218 u_long delay; /* ns */
219 long offset; /* ns */
238 enum oncore_model model;
242 u_char chan; /* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */
243 s_char traim; /* do we have traim? yes UT/VP, no BASIC, GT, -1 unknown, 0 no, +1 yes */
244 u_char traim_delay; /* seconds counter, waiting for reply */
246 struct RSM rsm; /* bits extracted from Receiver Status Msg in @@Ea */
253 u_char Ea[160]; /* Ba, Ea or Ha */
254 u_char En[70]; /* Bn or En */
262 u_int timeout; /* count to retry Cj after Fa self-test */
263 u_char count; /* cycles thru Ea before starting */
268 #define rcvbuf instance->Rcvbuf
269 #define rcvptr instance->Rcvptr
271 static void oncore_consume P((struct instance *));
272 static void oncore_poll P((int, struct peer *));
273 static void oncore_read_config P((struct instance *));
274 static void oncore_receive P((struct recvbuf *));
275 static void oncore_sendmsg P((int fd, u_char *, size_t));
276 static void oncore_shutdown P((int, struct peer *));
277 static int oncore_start P((int, struct peer *));
278 static void oncore_get_timestamp P((struct instance *, long, long));
279 static void oncore_init_shmem P((struct instance *));
280 static void oncore_print_As P((struct instance *));
282 static void oncore_msg_any P((struct instance *, u_char *, size_t, int));
283 static void oncore_msg_As P((struct instance *, u_char *, size_t));
284 static void oncore_msg_At P((struct instance *, u_char *, size_t));
285 static void oncore_msg_Ay P((struct instance *, u_char *, size_t));
286 static void oncore_msg_Az P((struct instance *, u_char *, size_t));
287 static void oncore_msg_BaEaHa P((struct instance *, u_char *, size_t));
288 static void oncore_msg_Bj P((struct instance *, u_char *, size_t));
289 static void oncore_msg_BnEn P((struct instance *, u_char *, size_t));
290 static void oncore_msg_CaFaIa P((struct instance *, u_char *, size_t));
291 static void oncore_msg_Cb P((struct instance *, u_char *, size_t));
292 static void oncore_msg_Cf P((struct instance *, u_char *, size_t));
293 static void oncore_msg_Cj P((struct instance *, u_char *, size_t));
294 static void oncore_msg_Cj_id P((struct instance *, u_char *, size_t));
295 static void oncore_msg_Cj_init P((struct instance *, u_char *, size_t));
296 static void oncore_msg_Gj P((struct instance *, u_char *, size_t));
297 static void oncore_msg_Sz P((struct instance *, u_char *, size_t));
299 struct refclock refclock_oncore = {
300 oncore_start, /* start up driver */
301 oncore_shutdown, /* shut down driver */
302 oncore_poll, /* transmit poll message */
303 noentry, /* not used */
304 noentry, /* not used */
305 noentry, /* not used */
306 NOFLAGS /* not used */
310 * Understanding the next bit here is not easy unless you have a manual
311 * for the the various Oncore Models.
314 static struct msg_desc {
317 void (*handler) P((struct instance *, u_char *, size_t));
320 } oncore_messages[] = {
321 /* Ea and En first since they're most common */
322 { "Ea", 76, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
323 { "Ba", 68, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC" },
324 { "Ha", 154, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC" },
325 { "En", 69, oncore_msg_BnEn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
326 { "Bn", 59, oncore_msg_BnEn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC" },
332 { "As", 20, oncore_msg_As, "" },
333 { "At", 8, oncore_msg_At, "" },
337 { "Ay", 11, oncore_msg_Ay, "" },
338 { "Az", 11, oncore_msg_Az, "" },
341 { "Bj", 8, oncore_msg_Bj, "" },
342 { "Ca", 9, oncore_msg_CaFaIa, "" },
343 { "Cb", 33, oncore_msg_Cb, "" },
344 { "Cf", 7, oncore_msg_Cf, "" },
347 { "Cj", 294, oncore_msg_Cj, "" },
349 { "Fa", 9, oncore_msg_CaFaIa, "" },
351 { "Gj", 21, oncore_msg_Gj, "" },
352 { "Ia", 10, oncore_msg_CaFaIa, "" },
353 { "Sz", 8, oncore_msg_Sz, "" },
360 u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 };
361 u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 };
362 u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 };
363 u_char oncore_cmd_Ga[] = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };
367 * Start automatic site survey
369 static u_char oncore_cmd_At0[] = { 'A', 't', 0 }; /* Posn Hold off */
370 static u_char oncore_cmd_At1[] = { 'A', 't', 1 }; /* Posn Hold on */
371 static u_char oncore_cmd_At2[] = { 'A', 't', 2 }; /* Start Site Survey */
374 * 0D/2D Position and Set.
376 u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };
377 u_char oncore_cmd_Asx[]= { 'A', 's', 0x7f, 0xff, 0xff, 0xff,
378 0x7f, 0xff, 0xff, 0xff,
379 0x7f, 0xff, 0xff, 0xff, 0xff };
380 u_char oncore_cmd_Au[] = { 'A', 'u', 0,0,0,0,0 };
382 u_char oncore_cmd_Av0[] = { 'A', 'v', 0 };
383 u_char oncore_cmd_Av1[] = { 'A', 'v', 1 };
385 u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 }; /* 3D */
386 u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 }; /* 0D */
387 u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 }; /* 2D */
390 * Set to UTC time (not GPS).
392 u_char oncore_cmd_Aw[] = { 'A', 'w', 1 };
395 * Output Almanac when it changes
397 u_char oncore_cmd_Be[] = { 'B', 'e', 1 };
400 * Read back PPS Offset for Output
402 u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 };
403 u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };
406 * Read back Cable Delay for Output
408 u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 };
409 u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff };
412 * Application type = static.
414 u_char oncore_cmd_AB[] = { 'A', 'B', 4 };
417 * Visible Satellite Status Msg.
419 u_char oncore_cmd_Bb[] = { 'B', 'b', 1 };
422 * Leap Second Pending Message
423 * Request message once
425 u_char oncore_cmd_Bj[] = { 'B', 'j', 0 };
426 u_char oncore_cmd_Gj[] = { 'G', 'j' };
431 static u_char oncore_cmd_Cf[] = { 'C', 'f' };
434 * Set to Position Fix mode (only needed on VP).
436 u_char oncore_cmd_Cg[] = { 'C', 'g', 1 };
441 static u_char oncore_cmd_Cj[] = { 'C', 'j' };
444 * Position/Status/Data message
445 * Send once per second
447 static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 };
448 static u_char oncore_cmd_Ba[] = { 'B', 'a', 1 };
449 static u_char oncore_cmd_Ha[] = { 'H', 'a', 1 };
450 static u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 };
451 static u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 };
454 * Position/Status Extension Msg
456 u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */
459 * Time Raim Setup & Status Message
460 * Send once per second
463 * PPS on when we have the first sat
465 static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
466 static u_char oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
467 static u_char oncore_cmd_Bn[] = { 'B', 'n', 1, 1, 0,10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
468 static u_char oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
473 static u_char oncore_cmd_Ca[] = { 'C', 'a' }; /* 6 Chan */
474 static u_char oncore_cmd_Fa[] = { 'F', 'a' }; /* 8 Chan */
475 static u_char oncore_cmd_Ia[] = { 'I', 'a' }; /* 12 Chan */
477 #define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */
478 #define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */
479 #define INIT_FILE "/etc/ntp.oncore" /* optional init file */
481 #define SPEED B9600 /* Oncore Binary speed (9600 bps) */
484 * Assemble and disassemble 32bit signed quantities from a buffer.
488 /* to buffer, int w, u_char *buf */
489 #define w32_buf(buf,w) { u_int i_tmp; \
490 i_tmp = (w<0) ? (~(-w)+1) : (w); \
491 (buf)[0] = (i_tmp >> 24) & 0xff; \
492 (buf)[1] = (i_tmp >> 16) & 0xff; \
493 (buf)[2] = (i_tmp >> 8) & 0xff; \
494 (buf)[3] = (i_tmp ) & 0xff; \
497 #define w32(buf) (((buf)[0]&0xff) << 24 | \
498 ((buf)[1]&0xff) << 16 | \
499 ((buf)[2]&0xff) << 8 | \
502 /* from buffer, char *buf, result to an int */
503 #define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))
505 extern int pps_assert;
506 extern int pps_hardpps;
511 * oncore_start - initialize data for processing
520 register struct instance *instance;
521 struct refclockproc *pp;
523 char device1[30], device2[30];
525 struct stat stat1, stat2;
528 /* opening different devices for fd1 and fd2 presents no problems */
529 /* opening the SAME device twice, seems to be OS dependent.
530 (a) on Linux (no streams) no problem
531 (b) on SunOS (and possibly Solaris, untested), (streams)
532 never see the line discipline.
533 Since things ALWAYS work if we only open the device once, we check
534 to see if the two devices are in fact the same, then proceed to
538 (void)sprintf(device1, DEVICE1, unit);
539 (void)sprintf(device2, DEVICE2, unit);
541 if (stat(device1, &stat1)) {
542 perror("ONCORE: stat fd1");
546 if (stat(device2, &stat2)) {
547 perror("ONCORE: stat fd2");
551 if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) {
552 /* same device here */
553 if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW
554 #if !defined(HAVE_PPSAPI) && !defined(TIOCDCDTIMESTAMP)
558 perror("ONCORE: fd1");
562 } else { /* different devices here */
563 if (!(fd1=refclock_open(device1, SPEED, LDISC_RAW))) {
564 perror("ONCORE: fd1");
567 if ((fd2=open(device2, O_RDWR)) < 0) {
568 perror("ONCORE: fd2");
573 /* Devices now open, create instance structure for this unit */
575 if (!(instance = (struct instance *) malloc(sizeof *instance))) {
580 memset((char *) instance, 0, sizeof *instance);
582 /* link instance up and down */
585 pp->unitptr = (caddr_t) instance;
587 instance->unit = unit;
588 instance->peer = peer;
590 /* initialize miscellaneous variables */
592 instance->o_state = ONCORE_NO_IDEA;
593 cp = "state = ONCORE_NO_IDEA";
594 record_clock_stats(&(instance->peer->srcadr), cp);
596 instance->ttyfd = fd1;
597 instance->ppsfd = fd2;
599 instance->Bj_day = -1;
600 instance->assert = pps_assert;
601 instance->traim = -1;
602 instance->model = ONCORE_UNKNOWN;
603 instance->mode = MODE_UNKNOWN;
604 instance->site_survey = ONCORE_SS_UNKNOWN;
606 peer->precision = -26;
609 pp->clockdesc = "Motorola Oncore GPS Receiver";
610 memcpy((char *)&pp->refid, "GPS\0", (size_t) 4);
612 /* go read any input data in /etc/ntp.oncoreX */
614 oncore_read_config(instance);
617 if (time_pps_create(fd2, &instance->pps_h) < 0) {
618 perror("time_pps_create");
622 if (time_pps_getcap(instance->pps_h, &mode) < 0) {
624 "refclock_ioctl: time_pps_getcap failed: %m");
628 if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
630 "refclock_ioctl: time_pps_getparams failed: %m");
634 /* nb. only turn things on, if someone else has turned something
635 * on before we get here, leave it alone!
638 if (instance->assert) { /* nb, default or ON */
639 instance->pps_p.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
640 instance->pps_p.assert_offset.tv_sec = 0;
641 instance->pps_p.assert_offset.tv_nsec = 0;
643 instance->pps_p.mode = PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
644 instance->pps_p.clear_offset.tv_sec = 0;
645 instance->pps_p.clear_offset.tv_nsec = 0;
647 instance->pps_p.mode |= PPS_TSFMT_TSPEC;
648 instance->pps_p.mode &= mode; /* only set what is legal */
650 if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
651 perror("time_pps_setparams");
656 if (stat(pps_device, &stat1)) {
657 perror("ONCORE: stat pps_device");
661 /* must have hardpps ON, and fd2 must be the same device as on the pps line */
663 if (pps_hardpps && ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino))) {
666 if (instance->assert)
667 i = PPS_CAPTUREASSERT;
669 i = PPS_CAPTURECLEAR;
672 if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
673 PPS_TSFMT_TSPEC) < 0) {
675 "refclock_ioctl: time_pps_kcbind failed: %m");
684 pp->io.clock_recv = oncore_receive;
685 pp->io.srcclock = (caddr_t)peer;
688 if (!io_addclock(&pp->io)) {
689 perror("io_addclock");
696 * This will return the Model Number of the Oncore receiver.
699 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
700 instance->o_state = ONCORE_ID_SENT;
701 cp = "state = ONCORE_ID SENT";
702 record_clock_stats(&(instance->peer->srcadr), cp);
703 instance->timeout = 4;
705 instance->pollcnt = 2;
712 * Read Input file if it exists.
717 struct instance *instance
721 * First we try to open the configuration file
723 * where N is the unit number viz 127.127.30.N.
724 * If we don't find it we try
729 * If we don't find any then we don't have the cable delay or PPS offset
730 * and we choose MODE (4) below.
732 * Five Choices for MODE
733 * (0) ONCORE is preinitialized, don't do anything to change it.
734 * nb, DON'T set 0D mode, DON'T set Delay, position...
735 * (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
736 * (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
737 * lock this in, go to 0D mode.
738 * (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
739 * (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
740 * lock this in, go to 0D mode.
741 * NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
742 * then this position is set as the INITIAL position of the ONCORE.
743 * This can reduce the time to first fix.
744 * -------------------------------------------------------------------------------
745 * Note that an Oncore UT without a battery backup retains NO information if it is
746 * power cycled, with a Battery Backup it remembers the almanac, etc.
747 * For an Oncore VP, there is an eeprom that will contain this data, along with the
748 * option of Battery Backup.
749 * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
750 * power cycle, since there is nowhere to store the data.
751 * -------------------------------------------------------------------------------
753 * If we open one or the other of the files, we read it looking for
754 * MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, STATUS,
755 * POSN3D, POSN2D, CHAN, TRAIM
756 * then initialize using method MODE. For Mode = (1,3) all of (LAT, LON, HT) must
757 * be present or mode reverts to (2,4).
761 * # is comment to end of line
762 * = allowed between 1st and 2nd fields.
764 * Expect to see one line with 'MODE' as first field, followed by an integer
765 * in the range 0-4 (default = 4).
767 * Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
768 * All numbers are floating point.
773 * Expect to see one line with 'HT' as first field,
774 * followed by 1-2 fields. First is a number, the second is 'FT' or 'M'
775 * for feet or meters. HT is the height above the GPS ellipsoid.
776 * If the reciever reports height in both GPS and MSL, then we will report
777 * the difference GPS-MSL on the clockstats file.
779 * There is an optional line, starting with DELAY, followed
780 * by 1 or two fields. The first is a number (a time) the second is
781 * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
782 * DELAY is cable delay, typically a few tens of ns.
784 * There is an optional line, starting with OFFSET, followed
785 * by 1 or two fields. The first is a number (a time) the second is
786 * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
787 * OFFSET is the offset of the PPS pulse from 0. (only fully implemented
788 * with the PPSAPI, we need to be able to tell the Kernel about this
789 * offset if the Kernel PLL is in use, but can only do this presently
790 * when using the PPSAPI interface. If not using the Kernel PLL,
791 * then there is no problem.
793 * There is an optional line, with either ASSERT or CLEAR on it, which
794 * determine which transition of the PPS signal is used for timing by the
795 * PPSAPI. If neither is present, then ASSERT is assumed.
797 * There are three options that have to do with using the shared memory opition.
798 * First, to enable the option there must be an ASSERT line with a file name.
799 * The file name is the file associated with the shared memory.
801 * In the shared memory there are three 'records' containing the @@Ea (or equivalent)
802 * data, and this contains the position data. There will always be data in the
803 * record cooresponding to the '0D' @@Ea record, and the user has a choice of
804 * filling the '3D' @@Ea record by specifying POSN3D, or the '2D' record by
805 * specifying POSN2D. In either case the '2D' or '3D' record is filled once
808 * Two additional variables that can be set are CHAN and TRAIM. These should be
809 * set correctly by the code examining the @@Cj record, but we bring them out here
810 * to allow the user to override either the # of channels, or the existance of TRAIM.
811 * CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be
812 * followed by YES or NO.
814 * So acceptable input would be
815 * # these are my coordinates (RWC)
818 * HT 1589 # could equally well say HT 5215 FT
823 char *cp, *cc, *ca, line[100], units[2], device[20], Msg[160];
824 int i, sign, lat_flg, long_flg, ht_flg, mode;
827 sprintf(device, "%s%d", INIT_FILE, instance->unit); /* try "ntp.oncore0" first */
828 if ((fd=fopen(device, "r")) == NULL) { /* it was in the original documentation */
829 sprintf(device, "%s.%d", INIT_FILE, instance->unit); /* then try "ntp.oncore.0 */
830 if ((fd=fopen(device, "r")) == NULL) {
831 if ((fd=fopen(INIT_FILE, "r")) == NULL) { /* and finally "ntp.oncore" */
832 instance->init_type = 4;
839 lat_flg = long_flg = ht_flg = 0;
840 while (fgets(line, 100, fd)) {
842 /* Remove comments */
843 if ((cp = strchr(line, '#')))
846 /* Remove trailing space */
847 for (i = strlen(line);
848 i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
852 /* Remove leading space */
853 for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
856 /* Stop if nothing left */
860 /* Uppercase the command and find the arg */
861 for (ca = cc; *ca; ca++) {
862 if (isascii((int)*ca)) {
863 if (islower((int)*ca)) {
865 } else if (isspace((int)*ca) || (*ca == '='))
870 /* Remove space (and possible =) leading the arg */
871 for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++)
875 * move call to oncore_shmem_init() from here to after
876 * we have determined Oncore Model, so we can ignore
877 * request if model doesnt 'support' it
880 if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) {
882 instance->shmem_fname = (char *) malloc((unsigned) (i+1));
883 strcpy(instance->shmem_fname, ca);
887 /* Uppercase argument as well */
888 for (cp = ca; *cp; cp++)
889 if (isascii((int)*cp) && islower((int)*cp))
892 if (!strncmp(cc, "LAT", (size_t) 3)) {
894 sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
900 instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
902 } else if (!strncmp(cc, "LON", (size_t) 3)) {
904 sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
910 instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
912 } else if (!strncmp(cc, "HT", (size_t) 2)) {
915 sscanf(ca, "%lf %1s", &f1, units);
918 instance->ss_ht = 100 * f1; /* cm */
920 } else if (!strncmp(cc, "DELAY", (size_t) 5)) {
923 sscanf(ca, "%lf %1s", &f1, units);
926 else if (units[0] == 'U')
928 else if (units[0] == 'M')
931 f1 = 1000000000 * f1;
932 if (f1 < 0 || f1 > 1.e9)
934 if (f1 < 0 || f1 > 999999) {
935 sprintf(Msg, "PPS Cable delay of %fns out of Range, ignored", f1);
936 record_clock_stats(&(instance->peer->srcadr), Msg);
938 instance->delay = f1; /* delay in ns */
939 } else if (!strncmp(cc, "OFFSET", (size_t) 6)) {
942 sscanf(ca, "%lf %1s", &f1, units);
945 else if (units[0] == 'U')
947 else if (units[0] == 'M')
950 f1 = 1000000000 * f1;
951 if (f1 < 0 || f1 > 1.e9)
953 if (f1 < 0 || f1 > 999999999.) {
954 sprintf(Msg, "PPS Offset of %fns out of Range, ignored", f1);
955 record_clock_stats(&(instance->peer->srcadr), Msg);
957 instance->offset = f1; /* offset in ns */
958 } else if (!strncmp(cc, "MODE", (size_t) 4)) {
959 sscanf(ca, "%d", &mode);
960 if (mode < 0 || mode > 4)
962 } else if (!strncmp(cc, "ASSERT", (size_t) 6)) {
963 instance->assert = 1;
964 } else if (!strncmp(cc, "CLEAR", (size_t) 5)) {
965 instance->assert = 0;
966 } else if (!strncmp(cc, "POSN2D", (size_t) 6)) {
967 instance->shmem_Posn = 2;
968 } else if (!strncmp(cc, "POSN3D", (size_t) 6)) {
969 instance->shmem_Posn = 3;
970 } else if (!strncmp(cc, "CHAN", (size_t) 4)) {
971 sscanf(ca, "%d", &i);
972 if ((i == 6) || (i == 8) || (i == 12))
974 } else if (!strncmp(cc, "TRAIM", (size_t) 5)) {
975 instance->traim = 1; /* so TRAIM alone is YES */
976 if (!strcmp(ca, "NO") || !strcmp(ca, "OFF")) /* Yes/No, On/Off */
983 * OK, have read all of data file, and extracted the good stuff.
984 * If lat/long/ht specified they ALL must be specified for mode = (1,3).
987 instance->posn_set = 1;
988 if ((lat_flg || long_flg || ht_flg) && !(lat_flg * long_flg * ht_flg)) {
989 printf("ONCORE: incomplete data on %s\n", INIT_FILE);
990 instance->posn_set = 0;
991 if (mode == 1 || mode == 3) {
992 sprintf(Msg, "Input Mode = %d, but no/incomplete position, mode set to %d", mode, mode+1);
993 record_clock_stats(&(instance->peer->srcadr), Msg);
997 instance->init_type = mode;
999 sprintf(Msg, "Input mode = %d", mode);
1000 record_clock_stats(&(instance->peer->srcadr), Msg);
1007 struct instance *instance
1010 #ifdef ONCORE_SHMEM_STATUS
1013 struct msg_desc *mp;
1014 size_t oncore_shmem_length;
1016 if (instance->shmem_first)
1019 instance->shmem_first++;
1021 if ((instance->statusfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
1022 perror(instance->shmem_fname);
1027 for (mp = oncore_messages; mp->flag[0]; mp++) {
1029 /* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */
1030 if (!strcmp(mp->flag, "Cb")) {
1031 instance->shmem_Cb = n;
1032 n += (mp->len + 3) * 34;
1034 if (!strcmp(mp->flag, "Ba")) {
1035 instance->shmem_Ba = n;
1036 n += (mp->len + 3) * 3;
1038 if (!strcmp(mp->flag, "Ea")) {
1039 instance->shmem_Ea = n;
1040 n += (mp->len + 3) * 3;
1042 if (!strcmp(mp->flag, "Ha")) {
1043 instance->shmem_Ha = n;
1044 n += (mp->len + 3) * 3;
1048 oncore_shmem_length = n + 2;
1049 fprintf(stderr, "ONCORE: SHMEM length: %d bytes\n", (int) oncore_shmem_length);
1051 buf = malloc(oncore_shmem_length);
1056 memset(buf, 0, sizeof(buf));
1057 i = write(instance->statusfd, buf, oncore_shmem_length);
1058 if (i != oncore_shmem_length) {
1059 perror(instance->shmem_fname);
1063 instance->shmem = (u_char *) mmap(0, oncore_shmem_length,
1064 PROT_READ | PROT_WRITE,
1065 #ifdef MAP_HASSEMAPHORE
1069 instance->statusfd, (off_t)0);
1070 if (instance->shmem == (u_char *)MAP_FAILED) {
1071 instance->shmem = 0;
1072 close (instance->statusfd);
1075 for (mp = oncore_messages; mp->flag[0]; mp++) {
1077 instance->shmem[l + 0] = mp->len >> 8;
1078 instance->shmem[l + 1] = mp->len & 0xff;
1079 instance->shmem[l + 2] = 0;
1080 instance->shmem[l + 3] = '@';
1081 instance->shmem[l + 4] = '@';
1082 instance->shmem[l + 5] = mp->flag[0];
1083 instance->shmem[l + 6] = mp->flag[1];
1084 if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) {
1085 if (!strcmp(mp->flag, "Cb"))
1089 for (i = 1; i < n; i++) {
1090 instance->shmem[l + i * (mp->len+3) + 0] = mp->len >> 8;
1091 instance->shmem[l + i * (mp->len+3) + 1] = mp->len & 0xff;
1092 instance->shmem[l + i * (mp->len+3) + 2] = 0;
1093 instance->shmem[l + i * (mp->len+3) + 3] = '@';
1094 instance->shmem[l + i * (mp->len+3) + 4] = '@';
1095 instance->shmem[l + i * (mp->len+3) + 5] = mp->flag[0];
1096 instance->shmem[l + i * (mp->len+3) + 6] = mp->flag[1];
1100 #endif /* ONCORE_SHMEM_STATUS */
1106 * oncore_shutdown - shut down the clock
1115 register struct instance *instance;
1116 struct refclockproc *pp;
1119 instance = (struct instance *) pp->unitptr;
1121 io_closeclock(&pp->io);
1129 * oncore_poll - called by the transmit procedure
1138 struct instance *instance;
1140 instance = (struct instance *) peer->procptr->unitptr;
1141 if (instance->timeout) {
1144 instance->timeout--;
1145 if (instance->timeout == 0) {
1146 cp = "Oncore: No response from @@Cj, shutting down driver";
1147 record_clock_stats(&(instance->peer->srcadr), cp);
1148 oncore_shutdown(unit, peer);
1150 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
1151 cp = "Oncore: Resend @@Cj";
1152 record_clock_stats(&(instance->peer->srcadr), cp);
1157 if (!instance->pollcnt)
1158 refclock_report(peer, CEVNT_TIMEOUT);
1160 instance->pollcnt--;
1161 peer->procptr->polls++;
1162 instance->polled = 1;
1168 * move data from NTP to buffer (toss in unlikely case it wont fit)
1173 struct recvbuf *rbufp
1179 struct instance *instance;
1181 peer = (struct peer *)rbufp->recv_srcclock;
1182 instance = (struct instance *) peer->procptr->unitptr;
1183 p = (u_char *) &rbufp->recv_space;
1188 printf("ONCORE: >>>");
1189 for(i=0; i<rbufp->recv_length; i++)
1190 printf("%02x ", p[i]);
1192 printf("ONCORE: >>>");
1193 for(i=0; i<rbufp->recv_length; i++)
1194 printf("%03o ", p[i]);
1199 i = rbufp->recv_length;
1200 if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf])
1201 i = sizeof(rcvbuf) - rcvptr; /* and some char will be lost */
1202 memcpy(rcvbuf+rcvptr, p, i);
1204 oncore_consume(instance);
1210 * Deal with any complete messages
1215 struct instance *instance
1221 while (rcvptr >= 7) {
1222 if (rcvbuf[0] != '@' || rcvbuf[1] != '@') {
1223 /* We're not in sync, lets try to get there */
1224 for (i=1; i < rcvptr-1; i++)
1225 if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@')
1228 printf("ONCORE[%d]: >>> skipping %d chars\n", instance->unit, i);
1230 memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i));
1235 /* Ok, we have a header now */
1236 l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1;
1238 if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2))
1242 printf("ONCORE[%d]: >>> Unknown MSG, skipping 4 (%c%c)\n", instance->unit, rcvbuf[2], rcvbuf[3]);
1243 memcpy(rcvbuf, rcvbuf+4, (size_t) 4);
1248 l = oncore_messages[m].len;
1251 printf("ONCORE[%d]: GOT: %c%c %d of %d entry %d\n", instance->unit, rcvbuf[2], rcvbuf[3], rcvptr, l, m);
1253 /* Got the entire message ? */
1258 /* are we at the end of message? should be <Cksum><CR><LF> */
1260 if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') {
1262 printf("ONCORE[%d]: NO <CR><LF> at end of message\n", instance->unit);
1263 } else { /* check the CheckSum */
1265 for (i = 2; i < l-3; i++)
1267 if (j == rcvbuf[l-3]) {
1268 if (instance->shmem != NULL) {
1269 instance->shmem[oncore_messages[m].shmem + 2]++;
1270 memcpy(instance->shmem + oncore_messages[m].shmem + 3,
1271 rcvbuf, (size_t) l);
1273 oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m);
1274 if (oncore_messages[m].handler)
1275 oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3));
1277 printf("ONCORE[%d]: Checksum mismatch! calc %o is %o\n", instance->unit, j, rcvbuf[l-3]);
1278 printf("ONCORE[%d]: @@%c%c ", instance->unit, rcvbuf[2], rcvbuf[3]);
1280 printf("%03o ", rcvbuf[i]);
1286 memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l));
1294 * write message to Oncore.
1306 printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], (int) len);
1307 write(fd, "@@", (size_t) 2);
1308 write(fd, ptr, len);
1311 write(fd, &cs, (size_t) 1);
1312 write(fd, "\r\n", (size_t) 2);
1318 * print Oncore response message.
1323 struct instance *instance,
1330 const char *fmt = oncore_messages[idx].fmt;
1332 #ifdef HAVE_GETCLOCK
1338 #ifdef HAVE_GETCLOCK
1339 (void) getclock(TIMEOFDAY, &ts);
1340 tv.tv_sec = ts.tv_sec;
1341 tv.tv_usec = ts.tv_nsec / 1000;
1343 GETTIMEOFDAY(&tv, 0);
1345 printf("ONCORE[%d]: %ld.%06ld\n", instance->unit, (long) tv.tv_sec, (long) tv.tv_usec);
1348 printf(">>@@%c%c ", buf[2], buf[3]);
1349 for(i=2; i < len && i < 2400 ; i++)
1350 printf("%02x", buf[i]);
1355 for (p = fmt; *p; p++) {
1359 printf("\n%c%c", buf[2], buf[3]);
1361 for (p = fmt; *p; p++) {
1362 printf("%02x", buf[i++]);
1372 * Demultiplex the almanac into shmem
1377 struct instance *instance,
1384 if (instance->shmem == NULL)
1389 else if (buf[4] == 4 && buf[5] <= 5)
1391 else if (buf[4] == 4 && buf[5] <= 10)
1396 instance->shmem[instance->shmem_Cb + i + 2]++;
1397 memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3));
1403 * We do an @@Cj twice in the initialization sequence.
1404 * o Once at the very beginning to get the Model number so we know what commands
1406 * o And once later after we have done a reset and test, (which may hang),
1407 * as we are about to initialize the Oncore and start it running.
1408 * o We have one routine below for each case.
1413 * Determine the Type from the Model #, this determines #chan and if TRAIM is
1414 * available. We use ONLY the #chans, and determint TRAIM by trying it.
1419 struct instance *instance,
1424 memcpy(instance->Cj, buf, len);
1426 instance->timeout = 0;
1427 if (instance->o_state == ONCORE_ID_SENT)
1428 oncore_msg_Cj_id(instance, buf, len);
1429 else if (instance->o_state == ONCORE_INIT)
1430 oncore_msg_Cj_init(instance, buf, len);
1435 /* The information on determing a Oncore 'Model', viz VP, UT, etc, from
1436 * the Model Number comes from "Richard M. Hambly" <rick@cnssys.com>
1437 * and from Motorola. Until recently Rick was the only source of
1438 * this information as Motorola didnt give the information out.
1443 struct instance *instance,
1448 char *cp, *cp1, *cp2, Model[21], Msg[160];
1451 /* Write Receiver ID message to clockstats file */
1453 instance->Cj[294] = '\0';
1454 for (cp=(char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
1455 cp1 = strchr(cp, '\r');
1457 cp1 = (char *)&instance->Cj[294];
1459 record_clock_stats(&(instance->peer->srcadr), cp);
1464 /* next, the Firmware Version and Revision numbers */
1466 instance->version = atoi(&instance->Cj[83]);
1467 instance->revision = atoi(&instance->Cj[111]);
1469 /* from model number decide which Oncore this is,
1470 and then the number of channels */
1472 for (cp=&instance->Cj[160]; *cp == ' '; cp++) /* start right after 'Model #' */
1476 for (; !isspace((int)*cp) && cp-cp1 < 20; cp++, cp2++)
1481 if (!strncmp(Model, "PVT6", (size_t) 4)) {
1483 instance->model = ONCORE_PVT6;
1484 } else if (Model[0] == 'A') {
1486 instance->model = ONCORE_BASIC;
1487 } else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) {
1489 instance->model = ONCORE_VP;
1490 } else if (!strncmp(Model, "P1", (size_t) 2)) {
1492 instance->model = ONCORE_M12;
1493 } else if (Model[0] == 'R') {
1494 if (Model[5] == 'N') {
1496 instance->model = ONCORE_GT;
1497 } else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') {
1499 instance->model = ONCORE_GTPLUS;
1500 } else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) {
1502 instance->model = ONCORE_UT;
1503 } else if (Model[1] == '5' && Model[5] == 'G') {
1505 instance->model = ONCORE_UTPLUS;
1506 } else if (Model[1] == '6' && Model[5] == 'G') {
1508 instance->model = ONCORE_SL;
1511 instance->model = ONCORE_UNKNOWN;
1515 instance->model = ONCORE_UNKNOWN;
1518 sprintf(Msg, "This looks like an Oncore %s with version %d.%d firmware.", cp, instance->version, instance->revision);
1519 record_clock_stats(&(instance->peer->srcadr), Msg);
1521 if (instance->chan == 0) { /* dont reset if set in input data */
1522 instance->chan = 8; /* default */
1523 if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
1525 else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
1527 else if (instance->model == ONCORE_M12)
1528 instance->chan = 12;
1531 if (instance->traim == -1) { /* dont reset if set in input data */
1532 instance->traim = 0; /* default */
1533 if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
1534 instance->traim = 0;
1535 else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
1536 instance->traim = 1;
1537 else if (instance->model == ONCORE_M12)
1538 instance->traim = 0;
1541 sprintf(Msg, "Channels = %d, TRAIM = %s", instance->chan,
1542 ((instance->traim < 0) ? "UNKNOWN" : ((instance->traim > 0) ? "ON" : "OFF")));
1543 record_clock_stats(&(instance->peer->srcadr), Msg);
1545 /* The M12 with 1.3 Firmware, looses track of all Satellites and has to
1546 * start again if we go from 0D -> 3D, then looses them again when we
1547 * go from 3D -> 0D. We do this to get a @@Ea message for SHMEM.
1548 * For NOW we have SHMEM turned off for the M12, v1.3
1551 /*BAD M12*/ if (instance->model == ONCORE_M12 && instance->version == 1 && instance->revision <= 3) {
1552 instance->shmem_fname = 0;
1553 cp = "*** SHMEM turned off for ONCORE M12 ***";
1554 record_clock_stats(&(instance->peer->srcadr), cp);
1558 * we now know model number and have zeroed
1559 * instance->shmem_fname if SHMEM is not supported
1562 if (instance->shmem_fname);
1563 oncore_init_shmem(instance);
1565 if (instance->shmem)
1566 cp = "SHMEM is available";
1568 cp = "SHMEM is NOT available";
1569 record_clock_stats(&(instance->peer->srcadr), cp);
1572 if (instance->assert)
1573 cp = "Timing on Assert.";
1575 cp = "Timing on Clear.";
1576 record_clock_stats(&(instance->peer->srcadr), cp);
1579 mode = instance->init_type;
1580 if (mode == 3 || mode == 4) { /* Cf will call Fa */
1581 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof(oncore_cmd_Cf));
1582 instance->o_state = ONCORE_RESET_SENT;
1583 cp = "state = ONCORE_RESET_SENT";
1584 record_clock_stats(&(instance->peer->srcadr), cp);
1586 if (instance->chan == 6)
1587 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ca, sizeof(oncore_cmd_Ca));
1588 else if (instance->chan == 8)
1589 oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof(oncore_cmd_Fa));
1590 else if (instance->chan == 12)
1591 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ia, sizeof(oncore_cmd_Ia));
1593 instance->o_state = ONCORE_TEST_SENT;
1594 cp = "state = ONCORE_TEST_SENT";
1595 record_clock_stats(&(instance->peer->srcadr), cp);
1596 instance->timeout = 4;
1604 struct instance *instance,
1609 char *cp, Cmd[20], Msg[160];
1612 /* OK, know type of Oncore, have possibly reset, and have tested.
1613 * If we have or don't have TRAIM and position hold may still be unknown.
1617 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
1618 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem */
1619 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off */
1620 oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time */
1621 oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static */
1622 oncore_sendmsg(instance->ttyfd, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem */
1624 /* Turn OFF position hold, it needs to be off to set position (for some units),
1625 will get set ON in @@Ea later */
1627 if (instance->chan == 12)
1628 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0));
1630 oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0));
1631 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
1634 mode = instance->init_type;
1636 printf("ONCORE[%d]: INIT mode = %d\n", instance->unit, mode);
1637 printf("ONCORE[%d]: chan = %d\n", instance->unit, instance->chan);
1640 /* If there is Position input in the Config file
1641 * and mode = (1,3) set it as posn hold posn, goto 0D mode.
1642 * or mode = (2,4) set it as INITIAL position, and do Site Survey.
1646 if (instance->posn_set) {
1647 switch (mode) { /* if we have a position, put it in as posn and posn-hold posn */
1654 memcpy(Cmd, oncore_cmd_As, sizeof(oncore_cmd_As)); /* dont modify static variables */
1655 w32_buf(&Cmd[2], (int) instance->ss_lat);
1656 w32_buf(&Cmd[6], (int) instance->ss_long);
1657 w32_buf(&Cmd[10], (int) instance->ss_ht);
1659 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_As));
1661 memcpy(Cmd, oncore_cmd_Au, sizeof(oncore_cmd_Au));
1662 w32_buf(&Cmd[2], (int) instance->ss_ht);
1664 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Au));
1666 if (instance->chan == 12) {
1667 memcpy(Cmd, oncore_cmd_Ga, sizeof(oncore_cmd_Ga));
1668 w32_buf(&Cmd[2], (int) instance->ss_lat);
1669 w32_buf(&Cmd[6], (int) instance->ss_long);
1670 w32_buf(&Cmd[10], (int) instance->ss_ht);
1672 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ga));
1673 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1));
1675 memcpy(Cmd, oncore_cmd_Ad, sizeof(oncore_cmd_Ad));
1676 w32_buf(&Cmd[2], (int) instance->ss_lat);
1677 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ad));
1679 memcpy(Cmd, oncore_cmd_Ae, sizeof(oncore_cmd_Ae));
1680 w32_buf(&Cmd[2], (int) instance->ss_long);
1681 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ae));
1683 memcpy(Cmd, oncore_cmd_Af, sizeof(oncore_cmd_Af));
1684 w32_buf(&Cmd[2], (int) instance->ss_ht);
1686 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Af));
1687 oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1));
1695 case 0: /* NO initialization, don't change anything */
1696 instance->site_survey = ONCORE_SS_DONE;
1700 case 3: /* Use given Position */
1701 instance->site_survey = ONCORE_SS_DONE;
1705 case 4: /* Site Survey */
1706 if (instance->chan == 12) { /* no 12chan site survey command */
1707 instance->site_survey = ONCORE_SS_SW;
1708 sprintf(Msg, "Initiating software 3D site survey (%d samples)", POS_HOLD_AVERAGE);
1709 record_clock_stats(&(instance->peer->srcadr), Msg);
1711 instance->site_survey = ONCORE_SS_TESTING;
1712 oncore_sendmsg(instance->ttyfd, oncore_cmd_At2, sizeof(oncore_cmd_At2));
1718 /* cable delay in ns */
1719 memcpy(Cmd, oncore_cmd_Az, sizeof(oncore_cmd_Az));
1720 w32_buf(&Cmd[2], instance->delay);
1721 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Az));
1723 /* PPS offset in ns */
1724 if (instance->offset) {
1725 if (instance->model == ONCORE_VP || instance->model == ONCORE_UT ||
1726 instance->model == ONCORE_UTPLUS) {
1727 memcpy(Cmd, oncore_cmd_Ay, sizeof(oncore_cmd_Ay));
1728 w32_buf(&Cmd[2], instance->offset);
1729 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ay));
1731 cp = "Can only set PPS OFFSET for VP/UT/UT+, offset ignored";
1732 record_clock_stats(&(instance->peer->srcadr), cp);
1733 instance->offset = 0;
1738 /* 6, 8 12 chan - Position/Status/Data Output Message, 1/s */
1739 /* now we're really running */
1741 if (instance->chan == 6) { /* kill 8 chan commands, possibly testing VP in 6chan mode */
1742 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba, sizeof(oncore_cmd_Ba));
1743 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
1744 oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0));
1745 } else if (instance->chan == 8) { /* kill 6chan commands */
1746 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof(oncore_cmd_Ea));
1747 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
1748 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
1749 } else if (instance->chan == 12)
1750 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha, sizeof(oncore_cmd_Ha));
1752 instance->count = 1;
1753 instance->o_state = ONCORE_ALMANAC;
1754 cp = "state = ONCORE_ALMANAC";
1755 record_clock_stats(&(instance->peer->srcadr), cp);
1761 * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
1762 * not so for VP (eeprom) or any unit with a battery
1767 struct instance *instance,
1774 if (instance->o_state == ONCORE_RESET_SENT) {
1775 if (instance->chan == 6)
1776 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ca, sizeof(oncore_cmd_Ca));
1777 else if (instance->chan == 8)
1778 oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof(oncore_cmd_Fa));
1779 else if (instance->chan == 12)
1780 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ia, sizeof(oncore_cmd_Ia));
1782 instance->o_state = ONCORE_TEST_SENT;
1783 cp = "state = ONCORE_TEST_SENT";
1784 record_clock_stats(&(instance->peer->srcadr), cp);
1790 /* Here for @@Ca, @@Fa and @@Ia messages */
1792 /* There are good reasons NOT to do a @@Ca or @@Fa command with the ONCORE.
1793 * Doing it, it was found that under some circumstances the following
1794 * command would fail if issued immediately after the return from the
1795 * @@Fa, but a 2sec delay seemed to fix things. Since simply calling
1796 * sleep(2) is wastefull, and may cause trouble for some OS's, repeating
1797 * itimer, we set a flag, and test it at the next POLL. If it hasnt
1798 * been cleared, we reissue the @@Cj that is issued below.
1799 * Note that we do a @@Cj at the beginning, and again here.
1800 * The first is to get the info, the 2nd is just used as a safe command
1801 * after the @@Fa for all Oncores (and it was in this posn in the
1807 struct instance *instance,
1814 if (instance->o_state == ONCORE_TEST_SENT) {
1817 instance->timeout = 0;
1821 printf("ONCORE[%d]: >>@@%ca %x %x %x\n", instance->unit, buf[2], buf[4], buf[5], buf[6]);
1823 printf("ONCORE[%d]: >>@@%ca %x %x\n", instance->unit, buf[2], buf[4], buf[5]);
1826 antenna = buf[4] & 0xc0;
1830 if (buf[4] || buf[5] || ((buf[2] == 'I') && buf[6])) {
1831 cp = "ONCORE: Self Test Failed, shutting down driver";
1832 record_clock_stats(&(instance->peer->srcadr), cp);
1833 oncore_shutdown(instance->unit, instance->peer);
1837 char *cp1, Msg[160];
1839 cp1 = (antenna == 0x1) ? "(Over Current)" :
1840 ((antenna == 0x2) ? "(Under Current)" : "(No Voltage)");
1842 cp = "ONCORE: Self Test, NonFatal Antenna Problems ";
1845 record_clock_stats(&(instance->peer->srcadr), Msg);
1848 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
1849 instance->o_state = ONCORE_INIT;
1850 cp = "state = ONCORE_INIT";
1851 record_clock_stats(&(instance->peer->srcadr), cp);
1857 /* Ba, Ea and Ha come here */
1861 struct instance *instance,
1867 char Msg[160], Cmd[20];
1868 u_char *vp; /* pointer to start of shared mem for Ba/Ea/Ha */
1871 /* At the beginning of Ea here there are various 'timers'.
1872 * We enter Ea 1/sec, and since the upper levels of NTP have usurped
1873 * the use of timers, we use the 1/sec entry to Ea to do things that
1874 * we would normally do with timers...
1877 if (instance->count) {
1878 if (instance->count++ < 5) /* make sure results are stable, using position */
1880 instance->count = 0;
1883 if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
1886 Len = len+3; /* message length @@ -> CR,LF */
1887 memcpy(instance->Ea, buf, Len); /* Ba, Ea or Ha */
1889 if (buf[2] == 'B') { /* 6chan */
1890 if (instance->Ea[64]&0x8)
1891 instance->mode = MODE_0D;
1892 else if (instance->Ea[64]&0x10)
1893 instance->mode = MODE_2D;
1894 else if (instance->Ea[64]&0x20)
1895 instance->mode = MODE_3D;
1896 } else if (buf[2] == 'E') { /* 8chan */
1897 if (instance->Ea[72]&0x8)
1898 instance->mode = MODE_0D;
1899 else if (instance->Ea[72]&0x10)
1900 instance->mode = MODE_2D;
1901 else if (instance->Ea[72]&0x20)
1902 instance->mode = MODE_3D;
1903 } else if (buf[2] == 'H') { /* 12chan */
1906 bits = (instance->Ea[129]>>5) & 0x7; /* actually Ha */
1908 instance->mode = MODE_0D;
1909 else if (bits == 0x6)
1910 instance->mode = MODE_2D;
1911 else if (bits == 0x7)
1912 instance->mode = MODE_3D;
1915 vp = (u_char) 0; /* just to keep compiler happy */
1916 if (instance->chan == 6) {
1917 instance->rsm.bad_almanac = instance->Ea[64]&0x1;
1918 instance->rsm.bad_fix = instance->Ea[64]&0x52;
1919 vp = &instance->shmem[instance->shmem_Ba];
1920 } else if (instance->chan == 8) {
1921 instance->rsm.bad_almanac = instance->Ea[72]&0x1;
1922 instance->rsm.bad_fix = instance->Ea[72]&0x52;
1923 vp = &instance->shmem[instance->shmem_Ea];
1924 } else if (instance->chan == 12) {
1927 bits1 = (instance->Ea[129]>>5) & 0x7; /* actually Ha */
1928 bits2 = instance->Ea[130];
1929 instance->rsm.bad_almanac = (bits2 & 0x80);
1930 instance->rsm.bad_fix = (bits2 & 0x8) || (bits1 == 0x2);
1931 /* too few sat Bad Geom */
1932 vp = &instance->shmem[instance->shmem_Ha];
1934 fprintf(stderr, "ONCORE: DEBUG BITS: (%x %x), (%x %x), %x %x %x %x %x\n",
1935 instance->Ea[129], instance->Ea[130], bits1, bits2, instance->mode == MODE_0D, instance->mode == MODE_2D,
1936 instance->mode == MODE_3D, instance->rsm.bad_almanac, instance->rsm.bad_fix);
1940 /* Here calculate dH = GPS - MSL for output message */
1941 /* also set Altitude Hold mode if GT */
1943 if (!instance->have_dH) {
1946 instance->have_dH++;
1947 if (instance->chan == 12) {
1948 GPS = buf_w32(&instance->Ea[39]);
1949 MSL = buf_w32(&instance->Ea[43]);
1951 GPS = buf_w32(&instance->Ea[23]);
1952 MSL = buf_w32(&instance->Ea[27]);
1954 instance->dH = GPS - MSL;
1955 instance->dH /= 100.;
1957 if (MSL) { /* not set ! */
1958 sprintf(Msg, "dH = (GPS - MSL) = %.2fm", instance->dH);
1959 record_clock_stats(&(instance->peer->srcadr), Msg);
1962 /* stuck in here as it only gets done once */
1964 if (instance->chan != 12 && !instance->saw_At) {
1965 cp = "Not Good, no @@At command, must be a GT/GT+";
1966 record_clock_stats(&(instance->peer->srcadr), cp);
1967 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
1972 * For instance->site_survey to be ONCORE_SS_TESTING, this must be the first
1973 * time thru @@Ea. There are two choices
1974 * (a) We did not get a response to the @@At0 or @@At2 commands,
1975 * must be a GT/GT+/SL with no position hold mode.
1976 * (b) Saw the @@At0, @@At2 commands, but @@At2 failed,
1977 * must be a VP or older UT which doesnt have Site Survey mode.
1978 * We will have to do it ourselves.
1981 if (instance->site_survey == ONCORE_SS_TESTING) { /* first time thru Ea */
1982 sprintf(Msg, "Initiating software 3D site survey (%d samples)",
1984 record_clock_stats(&(instance->peer->srcadr), Msg);
1985 instance->site_survey = ONCORE_SS_SW;
1987 instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
1988 oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */
1991 if (instance->shmem) {
1995 if (instance->mode == MODE_0D) /* 0D, Position Hold */
1997 else if (instance->mode == MODE_2D) /* 2D, Altitude Hold */
1999 else if (instance->mode == MODE_3D) /* 3D fix */
2004 memcpy(&vp[i+3], buf, Len);
2008 /* Almanac mode, waiting for Almanac, cant do anything till we have it */
2009 /* When we have an almanac, start the En/Bn messages */
2011 if (instance->o_state == ONCORE_ALMANAC) {
2012 if (instance->rsm.bad_almanac) {
2014 printf("ONCORE: waiting for almanac\n");
2016 } else { /* Here we have almanac.
2017 Start TRAIM (@@En/@@Bn) dependant on TRAIM flag.
2018 If flag == -1, then we dont know if this unit supports
2019 traim, and we issue the command and then wait up to
2020 5sec to see if we get a reply */
2022 if (instance->traim != 0) { /* either yes or unknown */
2023 if (instance->chan == 6)
2024 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn, sizeof(oncore_cmd_Bn));
2025 else if (instance->chan == 8)
2026 oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof(oncore_cmd_En));
2028 if (instance->traim == -1)
2029 instance->traim_delay = 1;
2031 instance->o_state = ONCORE_RUN;
2032 cp = "state = ONCORE_RUN";
2033 record_clock_stats(&(instance->peer->srcadr), cp);
2038 * check if timer active
2039 * if it hasnt been cleared, then @@En/@@Bn did not respond
2042 if (instance->traim_delay) {
2043 if (instance->traim_delay++ > 5) {
2044 instance->traim = 0;
2045 instance->traim_delay = 0;
2046 cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF";
2047 record_clock_stats(&(instance->peer->srcadr), cp);
2052 * must be ONCORE_RUN if we are here.
2055 instance->pp->year = buf[6]*256+buf[7];
2056 instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]);
2057 instance->pp->hour = buf[8];
2058 instance->pp->minute = buf[9];
2059 instance->pp->second = buf[10];
2062 * Check to see if Hardware SiteSurvey has Finished.
2065 if ((instance->site_survey == ONCORE_SS_HW) && !(instance->Ea[37] & 0x20)) {
2066 record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
2067 instance->site_survey = ONCORE_SS_DONE;
2070 if (!instance->printed && instance->site_survey == ONCORE_SS_DONE) {
2071 instance->printed = 1;
2072 /* Read back Position Hold Params (cant for GT) */
2073 if (instance->saw_At)
2074 oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx, sizeof(oncore_cmd_Asx));
2076 oncore_print_As(instance);
2078 /* Read back PPS Offset for Output */
2079 /* Nb. This will fail silently for early UT (no plus) and M12 models */
2080 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ayx, sizeof(oncore_cmd_Ayx));
2082 /* Read back Cable Delay for Output */
2083 oncore_sendmsg(instance->ttyfd, oncore_cmd_Azx, sizeof(oncore_cmd_Azx));
2087 * Check the leap second status once per day.
2090 if (instance->Bj_day != buf[5]) { /* do this 1/day */
2091 instance->Bj_day = buf[5];
2093 if (instance->chan == 12)
2094 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gj, sizeof(oncore_cmd_Gj));
2097 * The following additional check, checking for June/December, is a
2098 * workaround for incorrect ONCORE firmware. The oncore starts
2099 * reporting the leap second when the GPS satellite data message
2100 * (page 18, subframe 4) is updated to a date in the future, which
2101 * can be several months before the leap second. WWV and other
2102 * services seem to wait until the month of the event to turn
2103 * on their indicators (which is usually a single bit).
2106 if ((buf[4] == 6) || (buf[4] == 12))
2107 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
2112 * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
2115 if (instance->shmem && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE)) { /* dont screw up the SS by changing mode */
2116 if (instance->pp->second%15 == 3) { /* start the sequence */
2117 instance->shmem_reset = 1;
2118 if (instance->chan == 12) {
2119 if (instance->shmem_Posn == 2)
2120 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd2, sizeof(oncore_cmd_Gd2)); /* 2D */
2122 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* 3D */
2124 if (instance->saw_At) {
2125 oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* out of 0D to 3D mode */
2126 if (instance->shmem_Posn == 2)
2127 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); /* 3D to 2D mode */
2129 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
2131 } else if (instance->shmem_reset || (instance->mode != MODE_0D)) {
2132 instance->shmem_reset = 0;
2133 if (instance->chan == 12)
2134 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); /* 0D */
2136 if (instance->saw_At) {
2137 if (instance->mode == MODE_2D)
2138 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* 2D -> 3D or 0D */
2139 oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1)); /* to 0D mode */
2141 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
2146 if (instance->traim == 0) /* NO traim, go get tick */
2147 oncore_get_timestamp(instance, instance->offset, instance->offset);
2149 if (instance->site_survey != ONCORE_SS_SW)
2153 * We have to average our own position for the Position Hold Mode
2154 * We use Heights from the GPS ellipsoid.
2157 if (instance->rsm.bad_fix) /* Not if poor geometry or less than 3 sats */
2160 if (instance->mode != MODE_3D) /* Only 3D Fix */
2163 instance->ss_lat += buf_w32(&instance->Ea[15]);
2164 instance->ss_long += buf_w32(&instance->Ea[19]);
2165 instance->ss_ht += buf_w32(&instance->Ea[23]); /* GPS ellipsoid */
2166 instance->ss_count++;
2168 if (instance->ss_count != POS_HOLD_AVERAGE)
2171 instance->ss_lat /= POS_HOLD_AVERAGE;
2172 instance->ss_long /= POS_HOLD_AVERAGE;
2173 instance->ss_ht /= POS_HOLD_AVERAGE;
2175 sprintf(Msg, "Surveyed posn: lat %.3f long %.3f ht %.3f",
2176 instance->ss_lat, instance->ss_long, instance->ss_ht);
2177 record_clock_stats(&(instance->peer->srcadr), Msg);
2179 /* set newly determined position as 3D Position hold position */
2181 memcpy(Cmd, oncore_cmd_As, sizeof(oncore_cmd_As));
2182 w32_buf(&Cmd[2], (int) instance->ss_lat);
2183 w32_buf(&Cmd[6], (int) instance->ss_long);
2184 w32_buf(&Cmd[10], (int) instance->ss_ht);
2186 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_As));
2188 /* set height seperately for 2D */
2190 memcpy(Cmd, oncore_cmd_Au, sizeof(oncore_cmd_Au));
2191 w32_buf(&Cmd[2], (int) instance->ss_ht);
2193 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Au));
2195 /* and set Position Hold */
2197 if (instance->chan == 12)
2198 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1));
2200 if (instance->saw_At)
2201 oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1));
2203 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
2206 record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
2207 instance->site_survey = ONCORE_SS_DONE;
2214 struct instance *instance,
2222 if (instance->o_state != ONCORE_RUN)
2225 if (instance->traim_delay) { /* flag that @@En/@@Bn returned */
2226 instance->traim = 1;
2227 instance->traim_delay = 0;
2228 cp = "ONCORE: Detected TRAIM, TRAIM = ON";
2229 record_clock_stats(&(instance->peer->srcadr), cp);
2232 memcpy(instance->En, buf, len); /* En or Bn */
2234 /* If Time RAIM doesn't like it, don't trust it */
2236 if (instance->En[21])
2239 dt1 = instance->saw_tooth + instance->offset; /* dt this time step */
2240 instance->saw_tooth = (s_char) instance->En[25]; /* update for next time */
2241 dt2 = instance->saw_tooth + instance->offset; /* dt next time step */
2243 oncore_get_timestamp(instance, dt1, dt2);
2249 oncore_get_timestamp(
2250 struct instance *instance,
2251 long dt1, /* tick offset THIS time step */
2252 long dt2 /* tick offset NEXT time step */
2259 #ifdef HAVE_STRUCT_TIMESPEC
2260 struct timespec *tsp = 0;
2262 struct timeval *tsp = 0;
2266 pps_params_t current_params;
2267 struct timespec timeout;
2269 #else /* ! HAVE_PPSAPI */
2270 #ifdef HAVE_CIOGETEV
2271 struct ppsclockev ev;
2274 #ifdef HAVE_TIOCGPPSEV
2275 struct ppsclockev ev;
2278 #if TIOCDCDTIMESTAMP
2281 #endif /* ! HAVE_PPS_API */
2283 if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D))
2286 /* Don't do anything without an almanac to define the GPS->UTC delta */
2288 if (instance->rsm.bad_almanac)
2292 j = instance->ev_serial;
2294 timeout.tv_nsec = 0;
2295 if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i,
2297 printf("ONCORE: time_pps_fetch failed\n");
2301 if (instance->assert) {
2302 tsp = &pps_i.assert_timestamp;
2305 i = (u_long) pps_i.assert_sequence;
2306 #ifdef HAVE_STRUCT_TIMESPEC
2307 printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n",
2308 instance->unit, i, j,
2309 (long)tsp->tv_sec, (long)tsp->tv_nsec);
2311 printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n",
2312 instance->unit, i, j,
2313 (long)tsp->tv_sec, (long)tsp->tv_usec);
2317 if (pps_i.assert_sequence == j) {
2318 printf("ONCORE: oncore_get_timestamp, error serial pps\n");
2321 instance->ev_serial = pps_i.assert_sequence;
2323 tsp = &pps_i.clear_timestamp;
2326 i = (u_long) pps_i.clear_sequence;
2327 #ifdef HAVE_STRUCT_TIMESPEC
2328 printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n",
2329 instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_nsec);
2331 printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n",
2332 instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_usec);
2336 if (pps_i.clear_sequence == j) {
2337 printf("ONCORE: oncore_get_timestamp, error serial pps\n");
2340 instance->ev_serial = pps_i.clear_sequence;
2343 /* convert timespec -> ntp l_fp */
2347 ts.l_uf = dmy * 4294967296.0;
2348 ts.l_ui = tsp->tv_sec;
2350 alternate code for previous 4 lines is
2351 dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
2353 dmy = tsp->tv_sec; /* integer part */
2354 DTOLFP(dmy, &ts_tmp);
2355 L_ADD(&ts, &ts_tmp);
2357 dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
2359 ts.l_ui = tsp->tv_sec;
2362 # if defined(HAVE_TIOCGPPSEV) || defined(HAVE_CIOGETEV)
2363 j = instance->ev_serial;
2364 if (ioctl(instance->ppsfd, r, (caddr_t) &ev) < 0) {
2365 perror("ONCORE: IOCTL:");
2372 #ifdef HAVE_STRUCT_TIMESPEC
2373 printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
2374 ev.serial, j, tsp->tv_sec, tsp->tv_nsec);
2376 printf("ONCORE: serial/j (%d, %d) %ld.%06ld\n",
2377 ev.serial, j, tsp->tv_sec, tsp->tv_usec);
2380 if (ev.serial == j) {
2381 printf("ONCORE: oncore_get_timestamp, error serial pps\n");
2384 instance->ev_serial = ev.serial;
2386 /* convert timeval -> ntp l_fp */
2390 # if defined(TIOCDCDTIMESTAMP)
2391 if(ioctl(instance->ppsfd, TIOCDCDTIMESTAMP, &tv) < 0) {
2392 perror("ONCORE: ioctl(TIOCDCDTIMESTAMP)");
2398 #error "Cannot compile -- no PPS mechanism configured!"
2402 /* now have timestamp in ts */
2403 /* add in saw_tooth and offset, these will be ZERO if no TRAIM */
2405 /* saw_tooth not really necessary if using TIMEVAL */
2406 /* since its only precise to us, but do it anyway. */
2408 /* offset in ns, and is positive (late), we subtract */
2409 /* to put the PPS time transition back where it belongs */
2412 /* must hand the offset for the NEXT sec off to the Kernel to do */
2413 /* the addition, so that the Kernel PLL sees the offset too */
2415 if (instance->assert)
2416 instance->pps_p.assert_offset.tv_nsec = -dt2;
2418 instance->pps_p.clear_offset.tv_nsec = -dt2;
2420 /* The following code is necessary, and not just a time_pps_setparams,
2421 * using the saved instance->pps_p, since some other process on the
2422 * machine may have diddled with the mode bits (say adding something
2423 * that it needs). We take what is there and ADD what we need.
2424 * [[ The results from the time_pps_getcap is unlikely to change so
2425 * we could probably just save it, but I choose to do the call ]]
2426 * Unfortunately, there is only ONE set of mode bits in the kernel per
2427 * interface, and not one set for each open handle.
2429 * There is still a race condition here where we might mess up someone
2430 * elses mode, but if he is being careful too, he should survive.
2433 if (time_pps_getcap(instance->pps_h, ¤t_mode) < 0) {
2435 "refclock_ioctl: time_pps_getcap failed: %m");
2439 if (time_pps_getparams(instance->pps_h, ¤t_params) < 0) {
2441 "refclock_ioctl: time_pps_getparams failed: %m");
2445 /* or current and mine */
2446 current_params.mode |= instance->pps_p.mode;
2447 /* but only set whats legal */
2448 current_params.mode &= current_mode;
2450 current_params.assert_offset.tv_sec = 0;
2451 current_params.assert_offset.tv_nsec = -dt2;
2452 current_params.clear_offset.tv_sec = 0;
2453 current_params.clear_offset.tv_nsec = -dt2;
2455 if (time_pps_setparams(instance->pps_h, ¤t_params))
2456 perror("time_pps_setparams");
2458 /* if not PPSAPI, no way to inform kernel of OFFSET, just add the */
2459 /* offset for THIS second */
2462 DTOLFP(dmy, &ts_tmp);
2463 L_ADD(&ts, &ts_tmp);
2465 /* have time from UNIX origin, convert to NTP origin. */
2467 ts.l_ui += JAN_1970;
2468 instance->pp->lastrec = ts;
2469 instance->pp->msec = 0;
2472 ts_tmp.l_ui = 0; /* zero integer part */
2473 LFPTOD(&ts_tmp, dmy); /* convert fractional part to a double */
2474 j = 1.0e9*dmy; /* then to integer ns */
2477 if (instance->chan == 6)
2478 Rsm = instance->Ea[64];
2479 else if (instance->chan == 8)
2480 Rsm = instance->Ea[72];
2481 else if (instance->chan == 12)
2482 Rsm = ((instance->Ea[129]<<8) | instance->Ea[130]);
2484 if (instance->chan == 6 || instance->chan == 8) {
2485 sprintf(instance->pp->a_lastcode, /* MAX length 128, currently at 117 */
2486 "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d sigma %d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d",
2488 instance->pp->year, instance->pp->day,
2489 instance->pp->hour, instance->pp->minute, instance->pp->second,
2490 (long) tsp->tv_sec % 60,
2491 Rsm, 0.1*(256*instance->Ea[35]+instance->Ea[36]),
2493 instance->Ea[38], instance->Ea[39], instance->En[21],
2494 /* nsat visible, nsat tracked, traim */
2495 instance->En[23]*256+instance->En[24], (s_char) instance->En[25],
2496 /* sigma neg-sawtooth */
2497 /*sat*/ instance->Ea[41], instance->Ea[45], instance->Ea[49], instance->Ea[53],
2498 instance->Ea[57], instance->Ea[61], instance->Ea[65], instance->Ea[69]
2499 ); /* will be 0 for 6 chan */
2500 } else if (instance->chan == 12) {
2501 sprintf(instance->pp->a_lastcode,
2502 "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d sat %d%d%d%d%d%d%d%d%d%d%d%d",
2504 instance->pp->year, instance->pp->day,
2505 instance->pp->hour, instance->pp->minute, instance->pp->second,
2506 (long) tsp->tv_sec % 60,
2507 Rsm, 0.1*(256*instance->Ea[53]+instance->Ea[54]),
2509 instance->Ea[55], instance->Ea[56],
2510 /* nsat visible, nsat tracked */
2511 /*sat*/ instance->Ea[58], instance->Ea[64], instance->Ea[70], instance->Ea[76],
2512 instance->Ea[82], instance->Ea[88], instance->Ea[94], instance->Ea[100],
2513 instance->Ea[106], instance->Ea[112], instance->Ea[118], instance->Ea[124]
2519 n = strlen(instance->pp->a_lastcode);
2520 printf("ONCORE[%d]: len = %d %s\n", instance->unit, n, instance->pp->a_lastcode);
2523 if (!refclock_process(instance->pp)) {
2524 refclock_report(instance->peer, CEVNT_BADTIME);
2528 record_clock_stats(&(instance->peer->srcadr), instance->pp->a_lastcode);
2529 instance->pollcnt = 2;
2531 if (instance->polled) {
2532 instance->polled = 0;
2534 instance->pp->dispersion = instance->pp->skew = 0;
2536 refclock_receive(instance->peer);
2543 * Try to use Oncore UT+ Auto Survey Feature
2544 * If its not there (VP), set flag to do it ourselves.
2549 struct instance *instance,
2554 instance->saw_At = 1;
2555 if (instance->site_survey == ONCORE_SS_TESTING) {
2557 record_clock_stats(&(instance->peer->srcadr),
2558 "Initiating hardware 3D site survey");
2559 instance->site_survey = ONCORE_SS_HW;
2566 /* get leap-second warning message */
2569 * @@Bj does NOT behave as documented in current Oncore firmware.
2570 * It turns on the LEAP indicator when the data is set, and does not,
2571 * as documented, wait until the beginning of the month when the
2572 * leap second will occur.
2573 * Until this firmware bug is fixed, @@Bj is only called in June/December.
2578 struct instance *instance,
2587 instance->peer->leap = LEAP_ADDSECOND;
2588 cp = "Set peer.leap to LEAP_ADDSECOND";
2591 instance->peer->leap = LEAP_DELSECOND;
2592 cp = "Set peer.leap to LEAP_DELSECOND";
2596 instance->peer->leap = LEAP_NOWARNING;
2597 cp = "Set peer.leap to LEAP_NOWARNING";
2600 record_clock_stats(&(instance->peer->srcadr), cp);
2603 /* Leap Second for M12, gives all info from satellite message */
2607 struct instance *instance,
2614 static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jly",
2615 "Aug", "Sep", "Oct", "Nov", "Dec" };
2617 /* print the message to verify whats there */
2619 dt = buf[5] - buf[4];
2622 sprintf(Msg, "ONCORE[%d]: Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d",
2624 buf[4], buf[5], 256*buf[6]+buf[7], buf[8], buf[9], buf[10],
2625 (buf[14]+256*(buf[13]+256*(buf[12]+256*buf[11]))),
2626 buf[15], buf[16], buf[17]);
2627 record_clock_stats(&(instance->peer->srcadr), Msg);
2630 sprintf(Msg, "ONCORE[%d]: Leap second (%d) scheduled for %d%s%d at %d:%d:%d",
2632 dt, buf[9], Month[buf[8]], 256*buf[6]+buf[7],
2633 buf[15], buf[16], buf[17]);
2634 record_clock_stats(&(instance->peer->srcadr), Msg);
2637 /* Only raise warning within a month of the leap second */
2639 instance->peer->leap = LEAP_NOWARNING;
2640 cp = "Set peer.leap to LEAP_NOWARNING";
2642 if (buf[6] == instance->Ea[6] && buf[7] == instance->Ea[7] && /* year */
2643 buf[8] == instance->Ea[4]) { /* month */
2646 instance->peer->leap = LEAP_DELSECOND;
2647 cp = "Set peer.leap to LEAP_DELSECOND";
2649 instance->peer->leap = LEAP_ADDSECOND;
2650 cp = "Set peer.leap to LEAP_ADDSECOND";
2654 record_clock_stats(&(instance->peer->srcadr), cp);
2660 * get Position hold position
2665 struct instance *instance,
2670 if (!instance->printed || instance->As)
2675 instance->ss_lat = buf_w32(&buf[4]);
2676 instance->ss_long = buf_w32(&buf[8]);
2677 instance->ss_ht = buf_w32(&buf[12]);
2679 /* Print out Position */
2680 oncore_print_As(instance);
2687 struct instance *instance
2690 char Msg[120], ew, ns;
2691 double xd, xm, xs, yd, ym, ys, hm, hft;
2692 int idx, idy, is, imx, imy;
2695 record_clock_stats(&(instance->peer->srcadr), "Posn:");
2697 lon = instance->ss_long;
2704 lat = instance->ss_lat;
2710 hm = instance->ss_ht/100.;
2713 xd = lat/3600000.; /* lat, lon in int msec arc, ht in cm. */
2715 sprintf(Msg, "Lat = %c %11.7fdeg, Long = %c %11.7fdeg, Alt = %5.2fm (%5.2fft) GPS", ns, xd, ew, yd, hm, hft);
2716 record_clock_stats(&(instance->peer->srcadr), Msg);
2724 sprintf(Msg, "Lat = %c %3ddeg %7.4fm, Long = %c %3ddeg %8.5fm, Alt = %7.2fm (%7.2fft) GPS", ns, idx, xm, ew, idy, ym, hm, hft);
2725 record_clock_stats(&(instance->peer->srcadr), Msg);
2733 sprintf(Msg, "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft);
2734 record_clock_stats(&(instance->peer->srcadr), Msg);
2741 * Nb. @@Ay is not supported for early UT (no plus) model
2746 struct instance *instance,
2753 if (!instance->printed || instance->Ay)
2758 instance->offset = buf_w32(&buf[4]);
2760 sprintf(Msg, "PPS Offset is set to %ld ns", instance->offset);
2761 record_clock_stats(&(instance->peer->srcadr), Msg);
2772 struct instance *instance,
2779 if (!instance->printed || instance->Az)
2784 instance->delay = buf_w32(&buf[4]);
2786 sprintf(Msg, "Cable delay is set to %ld ns", instance->delay);
2787 record_clock_stats(&(instance->peer->srcadr), Msg);
2792 struct instance *instance,
2799 cp = "Oncore: System Failure at Power On";
2800 if (instance && instance->peer) {
2801 record_clock_stats(&(instance->peer->srcadr), cp);
2802 oncore_shutdown(instance->unit, instance->peer);
2807 int refclock_oncore_bs;
2808 #endif /* REFCLOCK */