refclock_oncore.c revision 1.4
1/* $NetBSD: refclock_oncore.c,v 1.4 2011/07/01 02:18:37 mrg Exp $ */ 2 3 4/* 5 * ---------------------------------------------------------------------------- 6 * "THE BEER-WARE LICENSE" (Revision 42): 7 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 8 * can do whatever you want with this stuff. If we meet some day, and you think 9 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 10 * ---------------------------------------------------------------------------- 11 * 12 * refclock_oncore.c 13 * 14 * Driver for some of the various the Motorola Oncore GPS receivers. 15 * should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T 16 * The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate 17 * than the others. 18 * The receivers without position hold (GT, GT+) will be less accurate. 19 * 20 * Tested with: 21 * 22 * (UT) (VP) 23 * COPYRIGHT 1991-1997 MOTOROLA INC. COPYRIGHT 1991-1996 MOTOROLA INC. 24 * SFTW P/N # 98-P36848P SFTW P/N # 98-P36830P 25 * SOFTWARE VER # 2 SOFTWARE VER # 8 26 * SOFTWARE REV # 2 SOFTWARE REV # 8 27 * SOFTWARE DATE APR 24 1998 SOFTWARE DATE 06 Aug 1996 28 * MODEL # R1121N1114 MODEL # B4121P1155 29 * HWDR P/N # 1 HDWR P/N # _ 30 * SERIAL # R0010A SERIAL # SSG0226478 31 * MANUFACTUR DATE 6H07 MANUFACTUR DATE 7E02 32 * OPTIONS LIST IB 33 * 34 * (Basic) (M12) 35 * COPYRIGHT 1991-1994 MOTOROLA INC. COPYRIGHT 1991-2000 MOTOROLA INC. 36 * SFTW P/N # 98-P39949M SFTW P/N # 61-G10002A 37 * SOFTWARE VER # 5 SOFTWARE VER # 1 38 * SOFTWARE REV # 0 SOFTWARE REV # 3 39 * SOFTWARE DATE 20 JAN 1994 SOFTWARE DATE Mar 13 2000 40 * MODEL # A11121P116 MODEL # P143T12NR1 41 * HDWR P/N # _ HWDR P/N # 1 42 * SERIAL # SSG0049809 SERIAL # P003UD 43 * MANUFACTUR DATE 417AMA199 MANUFACTUR DATE 0C27 44 * OPTIONS LIST AB 45 * 46 * (M12+T) (M12+T later version) 47 * COPYRIGHT 1991-2002 MOTOROLA INC. COPYRIGHT 1991-2003 MOTOROLA INC. 48 * SFTW P/N # 61-G10268A SFTW P/N # 61-G10268A 49 * SOFTWARE VER # 2 SOFTWARE VER # 2 50 * SOFTWARE REV # 0 SOFTWARE REV # 1 51 * SOFTWARE DATE AUG 14 2002 SOFTWARE DATE APR 16 2003 52 * MODEL # P283T12T11 MODEL # P273T12T12 53 * HWDR P/N # 2 HWDR P/N # 2 54 * SERIAL # P04DC2 SERIAL # P05Z7Z 55 * MANUFACTUR DATE 2J17 MANUFACTUR DATE 3G15 56 * 57 * -------------------------------------------------------------------------- 58 * Reg Clemens (June 2009) 59 * BUG[1220] OK, big patch, but mostly done mechanically. Change direct calls to write 60 * to clockstats to a call to oncore_log, which now calls the old routine plus msyslog. 61 * Have to set the LOG_LEVELS of the calls for msyslog, and this was done by hand. New 62 * routine oncore_log. 63 * -------------------------------------------------------------------------- 64 * Reg Clemens (June 2009) 65 * BUG[1218] The comment on where the oncore driver gets its input file does not 66 * agree with the code. Change the comment. 67 * -------------------------------------------------------------------------- 68 * Reg Clemens (June 2009) 69 * change exit statements to return(0) in main program. I had assumed that if the 70 * PPS driver did not start for some reason, we shuould stop NTPD itelf. Others 71 * disagree. We now give an ERR log message and stop this driver. 72 * -------------------------------------------------------------------------- 73 * Reg Clemens (June 2009) 74 * A bytes available message for the input subsystem (Debug message). 75 * -------------------------------------------------------------------------- 76 * Reg Clemens (Nov 2008) 77 * This code adds a message for TRAIM messages. Users often worry about the 78 * driver not starting up, and it is often because of signal strength being low. 79 * Low signal strength will give TRAIM messages. 80 * -------------------------------------------------------------------------- 81 * Reg Clemens (Nov 2008) 82 * Add waiting on Almanac Message. 83 * -------------------------------------------------------------------------- 84 * Reg Clemens (Nov 2008) 85 * Add back in @@Bl code to do the @@Bj/@@Gj that is in later ONCOREs 86 * LEAP SECONDS: All of the ONCORE receivers, VP -> M12T have the @@Bj command 87 * that says 'Leap Pending'. As documented it only becomes true in the month 88 * before the leap second is to be applied, but in practice at least some of 89 * the receivers turn this indicator on as soon as the message is posted, which 90 * can be 6months early. As such, we use the Bj command to turn on the 91 * instance->pp->leap indicator but only run this test in December and June for 92 * updates on 1Jan and 1July. 93 * 94 * The @@Gj command exists in later ONCOREs, and it gives the exact date 95 * and size of the Leap Update. It can be emulated in the VP using the @@Bl 96 * command which reads the raw Satellite Broadcast Messages. 97 * We use these two commands to print informative messages in the clockstats 98 * file once per day as soon as the message appears on the satellites. 99 * -------------------------------------------------------------------------- 100 * Reg Clemens (Feb 2006) 101 * Fix some gcc4 compiler complaints 102 * Fix possible segfault in oncore_init_shmem 103 * change all (possible) fprintf(stderr, to record_clock_stats 104 * Apply patch from Russell J. Yount <rjy@cmu.edu> Fixed (new) MT12+T UTC not correct 105 * immediately after new Almanac Read. 106 * Apply patch for new PPS implementation by Rodolfo Giometti <giometti@linux.it> 107 * now code can use old Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de> or 108 * the new one. Compiles depending on timepps.h seen. 109 * -------------------------------------------------------------------------- 110 * Luis Batanero Guerrero <luisba@rao.es> (Dec 2005) Patch for leap seconds 111 * (the oncore driver was setting the wrong ntpd variable) 112 * -------------------------------------------------------------------------- 113 * Reg.Clemens (Mar 2004) 114 * Support for interfaces other than PPSAPI removed, for Solaris, SunOS, 115 * SCO, you now need to use one of the timepps.h files in the root dir. 116 * this driver will 'grab' it for you if you dont have one in /usr/include 117 * -------------------------------------------------------------------------- 118 * This code uses the two devices 119 * /dev/oncore.serial.n 120 * /dev/oncore.pps.n 121 * which may be linked to the same device. 122 * and can read initialization data from the file 123 * /etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where 124 * n or N are the unit number, viz 127.127.30.N. 125 * -------------------------------------------------------------------------- 126 * Reg.Clemens <reg@dwf.com> Sep98. 127 * Original code written for FreeBSD. 128 * With these mods it works on FreeBSD, SunOS, Solaris and Linux 129 * (SunOS 4.1.3 + ppsclock) 130 * (Solaris7 + MU4) 131 * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later). 132 * 133 * Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the 134 * state machine state) are printed to CLOCKSTATS if that file is enabled 135 * in /etc/ntp.conf. 136 * 137 * -------------------------------------------------------------------------- 138 * 139 * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13) 140 * doing an average of 10000 valid 2D and 3D fixes is what the automatic 141 * site survey mode does. Looking at the output from the receiver 142 * it seems like it is only using 3D fixes. 143 * When we do it ourselves, take 10000 3D fixes. 144 */ 145 146#define POS_HOLD_AVERAGE 10000 /* nb, 10000s ~= 2h45m */ 147 148/* 149 * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a 150 * "STATUS" line in the oncore config file, which contains the most recent 151 * copy of all types of messages we recognize. This file can be mmap(2)'ed 152 * by monitoring and statistics programs. 153 * 154 * See separate HTML documentation for this option. 155 */ 156 157#ifdef HAVE_CONFIG_H 158#include <config.h> 159#endif 160 161#if defined(REFCLOCK) && defined(CLOCK_ONCORE) 162 163#include "ntpd.h" 164#include "ntp_io.h" 165#include "ntp_unixtime.h" 166#include "ntp_refclock.h" 167#include "ntp_stdlib.h" 168 169#include <stdio.h> 170#include <ctype.h> 171#include <sys/stat.h> 172#ifdef ONCORE_SHMEM_STATUS 173# ifdef HAVE_SYS_MMAN_H 174# include <sys/mman.h> 175# ifndef MAP_FAILED 176# define MAP_FAILED ((u_char *) -1) 177# endif /* MAP_FAILED */ 178# endif /* HAVE_SYS_MMAN_H */ 179#endif /* ONCORE_SHMEM_STATUS */ 180 181#ifdef HAVE_PPSAPI 182# include "ppsapi_timepps.h" 183#endif 184 185#ifdef HAVE_SYS_SIO_H 186# include <sys/sio.h> 187#endif 188 189struct Bl { 190 int dt_ls; 191 int dt_lsf; 192 int WN; 193 int DN; 194 int WN_lsf; 195 int DN_lsf; 196 int wn_flg; 197 int lsf_flg; 198 int Bl_day; 199} Bl; 200 201enum receive_state { 202 ONCORE_NO_IDEA, 203 ONCORE_CHECK_ID, 204 ONCORE_CHECK_CHAN, 205 ONCORE_HAVE_CHAN, 206 ONCORE_RESET_SENT, 207 ONCORE_TEST_SENT, 208 ONCORE_INIT, 209 ONCORE_ALMANAC, 210 ONCORE_RUN 211}; 212 213enum site_survey_state { 214 ONCORE_SS_UNKNOWN, 215 ONCORE_SS_TESTING, 216 ONCORE_SS_HW, 217 ONCORE_SS_SW, 218 ONCORE_SS_DONE 219}; 220 221enum antenna_state { 222 ONCORE_ANTENNA_UNKNOWN = -1, 223 ONCORE_ANTENNA_OK = 0, 224 ONCORE_ANTENNA_OC = 1, 225 ONCORE_ANTENNA_UC = 2, 226 ONCORE_ANTENNA_NV = 3 227}; 228 229/* Model Name, derived from the @@Cj message. 230 * Used to initialize some variables. 231 */ 232 233enum oncore_model { 234 ONCORE_BASIC, 235 ONCORE_PVT6, 236 ONCORE_VP, 237 ONCORE_UT, 238 ONCORE_UTPLUS, 239 ONCORE_GT, 240 ONCORE_GTPLUS, 241 ONCORE_SL, 242 ONCORE_M12, 243 ONCORE_UNKNOWN 244}; 245 246/* the bits that describe these properties are in the same place 247 * on the VP/UT, but have moved on the M12. As such we extract 248 * them, and use them from this struct. 249 * 250 */ 251 252struct RSM { 253 u_char posn0D; 254 u_char posn2D; 255 u_char posn3D; 256 u_char bad_almanac; 257 u_char bad_fix; 258}; 259 260/* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to 261 * see what mode it is in. The bits on the M12 are multiplexed with 262 * other messages, so we have to 'keep' the last known mode here. 263 */ 264 265enum posn_mode { 266 MODE_UNKNOWN, 267 MODE_0D, 268 MODE_2D, 269 MODE_3D 270}; 271 272struct instance { 273 int unit; /* 127.127.30.unit */ 274 struct refclockproc *pp; 275 struct peer *peer; 276 277 int ttyfd; /* TTY file descriptor */ 278 int ppsfd; /* PPS file descriptor */ 279 int shmemfd; /* Status shm descriptor */ 280 pps_handle_t pps_h; 281 pps_params_t pps_p; 282 enum receive_state o_state; /* Receive state */ 283 enum posn_mode mode; /* 0D, 2D, 3D */ 284 enum site_survey_state site_survey; /* Site Survey state */ 285 enum antenna_state ant_state; /* antenna state */ 286 287 int Bj_day; 288 289 u_long delay; /* ns */ 290 long offset; /* ns */ 291 292 u_char *shmem; 293 char *shmem_fname; 294 u_int shmem_Cb; 295 u_int shmem_Ba; 296 u_int shmem_Ea; 297 u_int shmem_Ha; 298 u_char shmem_reset; 299 u_char shmem_Posn; 300 u_char shmem_bad_Ea; 301 u_char almanac_from_shmem; 302 303 double ss_lat; 304 double ss_long; 305 double ss_ht; 306 double dH; 307 int ss_count; 308 u_char posn_set; 309 310 enum oncore_model model; 311 u_int version; 312 u_int revision; 313 314 u_char chan; /* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */ 315 s_char traim; /* do we have traim? yes UT/VP, M12+T, no BASIC, GT, M12, -1 unknown, 0 no, +1 yes */ 316 /* the following 7 are all timing counters */ 317 u_char traim_delay; /* seconds counter, waiting for reply */ 318 u_char count; /* cycles thru Ea before starting */ 319 u_char count1; /* cycles thru Ea after SS_TESTING, waiting for SS_HW */ 320 u_char count2; /* cycles thru Ea after count, to check for @@Ea */ 321 u_char count3; /* cycles thru Ea checking for # channels */ 322 u_char count4; /* cycles thru leap after Gj to issue Bj */ 323 u_char count5; /* cycles thru get_timestamp waiting for valid UTC correction */ 324 u_char count5_set; /* only set count5 once */ 325 u_char counta; /* count for waiting on almanac message */ 326 u_char pollcnt; 327 u_char timeout; /* count to retry Cj after Fa self-test */ 328 u_char max_len; /* max length message seen by oncore_log, for debugging */ 329 u_char max_count; /* count for message statistics */ 330 331 struct RSM rsm; /* bits extracted from Receiver Status Msg in @@Ea */ 332 struct Bl Bl; /* Satellite Broadcast Data Message */ 333 u_char printed; 334 u_char polled; 335 u_long ev_serial; 336 unsigned Rcvptr; 337 u_char Rcvbuf[500]; 338 u_char BEHa[160]; /* Ba, Ea or Ha */ 339 u_char BEHn[80]; /* Bn , En , or Hn */ 340 u_char Cj[300]; 341 u_char Ag; /* Satellite mask angle */ 342 u_char saw_At; 343 u_char saw_Ay; 344 u_char saw_Az; 345 s_char saw_Bj; 346 s_char saw_Gj; 347 u_char have_dH; 348 u_char init_type; 349 s_char saw_tooth; 350 s_char chan_in; /* chan number from INPUT, will always use it */ 351 u_char chan_id; /* chan number determined from part number */ 352 u_char chan_ck; /* chan number determined by sending commands to hardware */ 353 s_char traim_in; /* TRAIM from INPUT, will always use ON/OFF specified */ 354 s_char traim_id; /* TRAIM determined from part number */ 355 u_char traim_ck; /* TRAIM determined by sending commands to hardware */ 356 u_char once; /* one pass code at top of BaEaHa */ 357 s_char assert; 358 u_char hardpps; 359}; 360 361#define rcvbuf instance->Rcvbuf 362#define rcvptr instance->Rcvptr 363 364static int oncore_start (int, struct peer *); 365static void oncore_poll (int, struct peer *); 366static void oncore_shutdown (int, struct peer *); 367static void oncore_consume (struct instance *); 368static void oncore_read_config (struct instance *); 369static void oncore_receive (struct recvbuf *); 370static int oncore_ppsapi (struct instance *); 371static void oncore_get_timestamp (struct instance *, long, long); 372static void oncore_init_shmem (struct instance *); 373 374static void oncore_antenna_report (struct instance *, enum antenna_state); 375static void oncore_chan_test (struct instance *); 376static void oncore_check_almanac (struct instance *); 377static void oncore_check_antenna (struct instance *); 378static void oncore_check_leap_sec (struct instance *); 379static int oncore_checksum_ok (u_char *, int); 380static void oncore_compute_dH (struct instance *); 381static void oncore_load_almanac (struct instance *); 382static void oncore_log (struct instance *, int, const char *); 383static void oncore_print_Cb (struct instance *, u_char *); 384/* static void oncore_print_array (u_char *, int); */ 385static void oncore_print_posn (struct instance *); 386static void oncore_sendmsg (struct instance *, u_char *, size_t); 387static void oncore_set_posn (struct instance *); 388static void oncore_set_traim (struct instance *); 389static void oncore_shmem_get_3D (struct instance *); 390static void oncore_ss (struct instance *); 391static int oncore_wait_almanac (struct instance *); 392 393static void oncore_msg_any (struct instance *, u_char *, size_t, int); 394static void oncore_msg_Adef (struct instance *, u_char *, size_t); 395static void oncore_msg_Ag (struct instance *, u_char *, size_t); 396static void oncore_msg_As (struct instance *, u_char *, size_t); 397static void oncore_msg_At (struct instance *, u_char *, size_t); 398static void oncore_msg_Ay (struct instance *, u_char *, size_t); 399static void oncore_msg_Az (struct instance *, u_char *, size_t); 400static void oncore_msg_BaEaHa (struct instance *, u_char *, size_t); 401static void oncore_msg_Bd (struct instance *, u_char *, size_t); 402static void oncore_msg_Bj (struct instance *, u_char *, size_t); 403static void oncore_msg_Bl (struct instance *, u_char *, size_t); 404static void oncore_msg_BnEnHn (struct instance *, u_char *, size_t); 405static void oncore_msg_CaFaIa (struct instance *, u_char *, size_t); 406static void oncore_msg_Cb (struct instance *, u_char *, size_t); 407static void oncore_msg_Cf (struct instance *, u_char *, size_t); 408static void oncore_msg_Cj (struct instance *, u_char *, size_t); 409static void oncore_msg_Cj_id (struct instance *, u_char *, size_t); 410static void oncore_msg_Cj_init (struct instance *, u_char *, size_t); 411static void oncore_msg_Ga (struct instance *, u_char *, size_t); 412static void oncore_msg_Gb (struct instance *, u_char *, size_t); 413static void oncore_msg_Gj (struct instance *, u_char *, size_t); 414static void oncore_msg_Sz (struct instance *, u_char *, size_t); 415 416struct refclock refclock_oncore = { 417 oncore_start, /* start up driver */ 418 oncore_shutdown, /* shut down driver */ 419 oncore_poll, /* transmit poll message */ 420 noentry, /* not used */ 421 noentry, /* not used */ 422 noentry, /* not used */ 423 NOFLAGS /* not used */ 424}; 425 426/* 427 * Understanding the next bit here is not easy unless you have a manual 428 * for the the various Oncore Models. 429 */ 430 431static struct msg_desc { 432 const char flag[3]; 433 const int len; 434 void (*handler) (struct instance *, u_char *, size_t); 435 const char *fmt; 436 int shmem; 437} oncore_messages[] = { 438 /* Ea and En first since they're most common */ 439 { "Ea", 76, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC", 0 }, 440 { "Ba", 68, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC", 0 }, 441 { "Ha", 154, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC", 0 }, 442 { "Bn", 59, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC", 0 }, 443 { "En", 69, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC", 0 }, 444 { "Hn", 78, oncore_msg_BnEnHn, "", 0 }, 445 { "Ab", 10, 0, "", 0 }, 446 { "Ac", 11, 0, "", 0 }, 447 { "Ad", 11, oncore_msg_Adef, "", 0 }, 448 { "Ae", 11, oncore_msg_Adef, "", 0 }, 449 { "Af", 15, oncore_msg_Adef, "", 0 }, 450 { "Ag", 8, oncore_msg_Ag, "", 0 }, /* Satellite mask angle */ 451 { "As", 20, oncore_msg_As, "", 0 }, 452 { "At", 8, oncore_msg_At, "", 0 }, 453 { "Au", 12, 0, "", 0 }, 454 { "Av", 8, 0, "", 0 }, 455 { "Aw", 8, 0, "", 0 }, 456 { "Ay", 11, oncore_msg_Ay, "", 0 }, 457 { "Az", 11, oncore_msg_Az, "", 0 }, 458 { "AB", 8, 0, "", 0 }, 459 { "Bb", 92, 0, "", 0 }, 460 { "Bd", 23, oncore_msg_Bd, "", 0 }, 461 { "Bj", 8, oncore_msg_Bj, "", 0 }, 462 { "Bl", 41, oncore_msg_Bl, "", 0 }, 463 { "Ca", 9, oncore_msg_CaFaIa, "", 0 }, 464 { "Cb", 33, oncore_msg_Cb, "", 0 }, 465 { "Cf", 7, oncore_msg_Cf, "", 0 }, 466 { "Cg", 8, 0, "", 0 }, 467 { "Ch", 9, 0, "", 0 }, 468 { "Cj", 294, oncore_msg_Cj, "", 0 }, 469 { "Ek", 71, 0, "", 0 }, 470 { "Fa", 9, oncore_msg_CaFaIa, "", 0 }, 471 { "Ga", 20, oncore_msg_Ga, "", 0 }, 472 { "Gb", 17, oncore_msg_Gb, "", 0 }, 473 { "Gc", 8, 0, "", 0 }, 474 { "Gd", 8, 0, "", 0 }, 475 { "Ge", 8, 0, "", 0 }, 476 { "Gj", 21, oncore_msg_Gj, "", 0 }, 477 { "Ia", 10, oncore_msg_CaFaIa, "", 0 }, 478 { "Sz", 8, oncore_msg_Sz, "", 0 }, 479 { {0}, 7, 0, "", 0 } 480}; 481 482 483static u_char oncore_cmd_Aa[] = { 'A', 'a', 0, 0, 0 }; /* 6/8 Time of Day */ 484static u_char oncore_cmd_Ab[] = { 'A', 'b', 0, 0, 0 }; /* 6/8 GMT Correction */ 485static u_char oncore_cmd_AB[] = { 'A', 'B', 4 }; /* VP Application Type: Static */ 486static u_char oncore_cmd_Ac[] = { 'A', 'c', 0, 0, 0, 0 }; /* 6/8 Date */ 487static u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 }; /* 6/8 Latitude */ 488static u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 }; /* 6/8 Longitude */ 489static u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 }; /* 6/8 Height */ 490static u_char oncore_cmd_Ag[] = { 'A', 'g', 0 }; /* 6/8/12 Satellite Mask Angle */ 491static u_char oncore_cmd_Agx[] = { 'A', 'g', 0xff }; /* 6/8/12 Satellite Mask Angle: read */ 492static u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 6/8/12 Posn Hold Parameters */ 493static u_char oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff, /* 6/8/12 Posn Hold Readback */ 494 0x7f,0xff,0xff,0xff, /* on UT+ this doesnt work with 0xff */ 495 0x7f,0xff,0xff,0xff, 0xff }; /* but does work with 0x7f (sigh). */ 496static u_char oncore_cmd_At0[] = { 'A', 't', 0 }; /* 6/8 Posn Hold: off */ 497static u_char oncore_cmd_At1[] = { 'A', 't', 1 }; /* 6/8 Posn Hold: on */ 498static u_char oncore_cmd_At2[] = { 'A', 't', 2 }; /* 6/8 Posn Hold: Start Site Survey */ 499static u_char oncore_cmd_Atx[] = { 'A', 't', 0xff }; /* 6/8 Posn Hold: Read Back */ 500static u_char oncore_cmd_Au[] = { 'A', 'u', 0,0,0,0, 0 }; /* GT/M12 Altitude Hold Ht. */ 501static u_char oncore_cmd_Av0[] = { 'A', 'v', 0 }; /* VP/GT Altitude Hold: off */ 502static u_char oncore_cmd_Av1[] = { 'A', 'v', 1 }; /* VP/GT Altitude Hold: on */ 503static u_char oncore_cmd_Aw[] = { 'A', 'w', 1 }; /* 6/8/12 UTC/GPS time selection */ 504static u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 }; /* Timing 1PPS time offset: set */ 505static u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff }; /* Timing 1PPS time offset: Read */ 506static u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 }; /* 6/8UT/12 1PPS Cable Delay: set */ 507static u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff }; /* 6/8UT/12 1PPS Cable Delay: Read */ 508static u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 }; /* 6 Position/Data/Status: off */ 509static u_char oncore_cmd_Ba[] = { 'B', 'a', 1 }; /* 6 Position/Data/Status: on */ 510static u_char oncore_cmd_Bb[] = { 'B', 'b', 1 }; /* 6/8/12 Visible Satellites */ 511static u_char oncore_cmd_Bd[] = { 'B', 'd', 1 }; /* 6/8/12? Almanac Status Msg. */ 512static u_char oncore_cmd_Be[] = { 'B', 'e', 1 }; /* 6/8/12 Request Almanac Data */ 513static u_char oncore_cmd_Bj[] = { 'B', 'j', 0 }; /* 6/8 Leap Second Pending */ 514static u_char oncore_cmd_Bl[] = { 'B', 'l', 1 }; /* VP Satellite Broadcast Data Msg */ 515static u_char oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim on */ 516static u_char oncore_cmd_Bn[] = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on, traim on */ 517static u_char oncore_cmd_Bnx[] = { 'B', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim off */ 518static u_char oncore_cmd_Ca[] = { 'C', 'a' }; /* 6 Self Test */ 519static u_char oncore_cmd_Cf[] = { 'C', 'f' }; /* 6/8/12 Set to Defaults */ 520static u_char oncore_cmd_Cg[] = { 'C', 'g', 1 }; /* VP Posn Fix/Idle Mode */ 521static u_char oncore_cmd_Cj[] = { 'C', 'j' }; /* 6/8/12 Receiver ID */ 522static u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 }; /* 8 Position/Data/Status: off */ 523static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 }; /* 8 Position/Data/Status: on */ 524static u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */ /* 8 Posn/Status/Data - extension */ 525static u_char oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim on */ 526static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on, traim on */ 527static u_char oncore_cmd_Enx[] = { 'E', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim off */ 528static u_char oncore_cmd_Fa[] = { 'F', 'a' }; /* 8 Self Test */ 529static u_char oncore_cmd_Ga[] = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 12 Position Set */ 530static u_char oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff, /* 12 Position Set: Read */ 531 0xff, 0xff, 0xff, 0xff, /* */ 532 0xff, 0xff, 0xff, 0xff, 0xff }; /* */ 533static u_char oncore_cmd_Gb[] = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* 12 set Date/Time */ 534static u_char oncore_cmd_Gc[] = { 'G', 'c', 1 }; /* 12 PPS Control: On Cont */ 535static u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 }; /* 12 Position Control: 3D (no hold) */ 536static u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 }; /* 12 Position Control: 0D (3D hold) */ 537static u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 }; /* 12 Position Control: 2D (Alt Hold) */ 538static u_char oncore_cmd_Gd3[] = { 'G', 'd', 3 }; /* 12 Position Coltrol: Start Site Survey */ 539static u_char oncore_cmd_Ge0[] = { 'G', 'e', 0 }; /* M12+T TRAIM: off */ 540static u_char oncore_cmd_Ge[] = { 'G', 'e', 1 }; /* M12+T TRAIM: on */ 541static u_char oncore_cmd_Gj[] = { 'G', 'j' }; /* 8?/12 Leap Second Pending */ 542static u_char oncore_cmd_Ha0[] = { 'H', 'a', 0 }; /* 12 Position/Data/Status: off */ 543static u_char oncore_cmd_Ha[] = { 'H', 'a', 1 }; /* 12 Position/Data/Status: on */ 544static u_char oncore_cmd_Hn0[] = { 'H', 'n', 0 }; /* 12 TRAIM Status: off */ 545static u_char oncore_cmd_Hn[] = { 'H', 'n', 1 }; /* 12 TRAIM Status: on */ 546static u_char oncore_cmd_Ia[] = { 'I', 'a' }; /* 12 Self Test */ 547 548/* it appears that as of 1997/1998, the UT had As,At, but not Au,Av 549 * the GT had Au,Av, but not As,At 550 * This was as of v2.0 of both firmware sets. possibly 1.3 for UT. 551 * Bj in UT at v1.3 552 * dont see Bd in UT/GT thru 1999 553 * Gj in UT as of 3.0, 1999 , Bj as of 1.3 554 */ 555 556static const char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jly", 557 "Aug", "Sep", "Oct", "Nov", "Dec" }; 558 559#define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */ 560#define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */ 561 562#define SPEED B9600 /* Oncore Binary speed (9600 bps) */ 563 564/* 565 * Assemble and disassemble 32bit signed quantities from a buffer. 566 * 567 */ 568 569 /* to buffer, int w, u_char *buf */ 570#define w32_buf(buf,w) { u_int i_tmp; \ 571 i_tmp = (w<0) ? (~(-w)+1) : (w); \ 572 (buf)[0] = (i_tmp >> 24) & 0xff; \ 573 (buf)[1] = (i_tmp >> 16) & 0xff; \ 574 (buf)[2] = (i_tmp >> 8) & 0xff; \ 575 (buf)[3] = (i_tmp ) & 0xff; \ 576 } 577 578#define w32(buf) (((buf)[0]&0xff) << 24 | \ 579 ((buf)[1]&0xff) << 16 | \ 580 ((buf)[2]&0xff) << 8 | \ 581 ((buf)[3]&0xff) ) 582 583 /* from buffer, char *buf, result to an int */ 584#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf)) 585 586 587/* 588 * oncore_start - initialize data for processing 589 */ 590 591static int 592oncore_start( 593 int unit, 594 struct peer *peer 595 ) 596{ 597#define STRING_LEN 32 598 register struct instance *instance; 599 struct refclockproc *pp; 600 int fd1, fd2; 601 char device1[STRING_LEN], device2[STRING_LEN], Msg[160]; 602 struct stat stat1, stat2; 603 604 /* create instance structure for this unit */ 605 606 instance = emalloc(sizeof(*instance)); 607 memset(instance, 0, sizeof(*instance)); 608 609 /* initialize miscellaneous variables */ 610 611 pp = peer->procptr; 612 pp->unitptr = (caddr_t) instance; 613 instance->pp = pp; 614 instance->unit = unit; 615 instance->peer = peer; 616 instance->assert = 1; 617 instance->once = 1; 618 619 instance->Bj_day = -1; 620 instance->traim = -1; 621 instance->traim_in = -1; 622 instance->chan_in = -1; 623 instance->model = ONCORE_UNKNOWN; 624 instance->mode = MODE_UNKNOWN; 625 instance->site_survey = ONCORE_SS_UNKNOWN; 626 instance->Ag = 0xff; /* Satellite mask angle, unset by user */ 627 instance->ant_state = ONCORE_ANTENNA_UNKNOWN; 628 629 peer->precision = -26; 630 peer->minpoll = 4; 631 peer->maxpoll = 4; 632 pp->clockdesc = "Motorola Oncore GPS Receiver"; 633 memcpy((char *)&pp->refid, "GPS\0", (size_t) 4); 634 635 oncore_log(instance, LOG_NOTICE, "ONCORE DRIVER -- CONFIGURING"); 636 instance->o_state = ONCORE_NO_IDEA; 637 638 /* Now open files. 639 * This is a bit complicated, a we dont want to open the same file twice 640 * (its a problem on some OS), and device2 may not exist for the new PPS 641 */ 642 643 (void)snprintf(device1, sizeof(device1), DEVICE1, unit); 644 (void)snprintf(device2, sizeof(device2), DEVICE2, unit); 645 646 /* OPEN DEVICES */ 647 /* opening different devices for fd1 and fd2 presents no problems */ 648 /* opening the SAME device twice, seems to be OS dependent. 649 (a) on Linux (no streams) no problem 650 (b) on SunOS (and possibly Solaris, untested), (streams) 651 never see the line discipline. 652 Since things ALWAYS work if we only open the device once, we check 653 to see if the two devices are in fact the same, then proceed to 654 do one open or two. 655 656 For use with linuxPPS we assume that the N_TTY file has been opened 657 and that the line discipline has been changed to N_PPS by another 658 program (say ppsldisc) so that the two files expected by the oncore 659 driver can be opened. 660 661 Note that the linuxPPS N_PPS file is just like a N_TTY, so we can do 662 the stat below without error even though the file has already had its 663 line discipline changed by another process. 664 665 The Windows port of ntpd arranges to return duplicate handles for 666 multiple opens of the same serial device, and doesn't have inodes 667 for serial handles, so we just open both on Windows. 668 */ 669#ifndef SYS_WINNT 670 if (stat(device1, &stat1)) { 671 snprintf(Msg, sizeof(Msg), "Can't stat fd1 (%s)", 672 device1); 673 oncore_log(instance, LOG_ERR, Msg); 674 return(0); /* exit, no file, can't start driver */ 675 } 676 677 if (stat(device2, &stat2)) { 678 stat2.st_dev = stat2.st_ino = -2; 679 snprintf(Msg, sizeof(Msg), 680 "Can't stat fd2 (%s) errno = %d", 681 device2, errno); 682 oncore_log(instance, LOG_ERR, Msg); 683 } 684#endif /* !SYS_WINNT */ 685 686 if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW))) { 687 snprintf(Msg, sizeof(Msg), "Can't open fd1 (%s)", 688 device1); 689 oncore_log(instance, LOG_ERR, Msg); 690 return(0); /* exit, can't open file, can't start driver */ 691 } 692 693 /* for LINUX the PPS device is the result of a line discipline. 694 It seems simplest to let an external program create the appropriate 695 /dev/pps<n> file, and only check (carefully) for its existance here 696 */ 697 698#ifndef SYS_WINNT 699 if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) /* same device here */ 700 fd2 = fd1; 701 else 702#endif /* !SYS_WINNT */ 703 { /* different devices here */ 704 if ((fd2=tty_open(device2, O_RDWR, 0777)) < 0) { 705 snprintf(Msg, sizeof(Msg), 706 "Can't open fd2 (%s)", device2); 707 oncore_log(instance, LOG_ERR, Msg); 708 return(0); /* exit, can't open PPS file, can't start driver */ 709 } 710 } 711 712 /* open ppsapi source */ 713 714 if (time_pps_create(fd2, &instance->pps_h) < 0) { 715 oncore_log(instance, LOG_ERR, "exit, PPSAPI not found in kernel"); 716 return(0); /* exit, don't find PPSAPI in kernel */ 717 } 718 719 /* continue initialization */ 720 721 instance->ttyfd = fd1; 722 instance->ppsfd = fd2; 723 724 /* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */ 725 726 oncore_read_config(instance); 727 728 if (!oncore_ppsapi(instance)) 729 return(0); 730 731 pp->io.clock_recv = oncore_receive; 732 pp->io.srcclock = (caddr_t)peer; 733 pp->io.datalen = 0; 734 pp->io.fd = fd1; 735 if (!io_addclock(&pp->io)) { 736 oncore_log(instance, LOG_ERR, "can't do io_addclock"); 737 (void) close(fd1); 738 free(instance); 739 return (0); 740 } 741 742#ifdef ONCORE_SHMEM_STATUS 743 /* 744 * Before starting ONCORE, lets setup SHMEM 745 * This will include merging an old SHMEM into the new one if 746 * an old one is found. 747 */ 748 749 oncore_init_shmem(instance); 750#endif 751 752 /* 753 * This will return the Model of the Oncore receiver. 754 * and start the Initialization loop in oncore_msg_Cj. 755 */ 756 757 instance->o_state = ONCORE_CHECK_ID; 758 oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_ID"); 759 760 instance->timeout = 4; 761 oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */ 762 oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 763 764 instance->pollcnt = 2; 765 return (1); 766} 767 768 769/* 770 * oncore_shutdown - shut down the clock 771 */ 772 773static void 774oncore_shutdown( 775 int unit, 776 struct peer *peer 777 ) 778{ 779 register struct instance *instance; 780 struct refclockproc *pp; 781 782 pp = peer->procptr; 783 instance = (struct instance *) pp->unitptr; 784 785 io_closeclock(&pp->io); 786 787 time_pps_destroy (instance->pps_h); 788 789 close(instance->ttyfd); 790 791 if ((instance->ppsfd != -1) && (instance->ppsfd != instance->ttyfd)) 792 close(instance->ppsfd); 793 794 if (instance->shmemfd) 795 close(instance->shmemfd); 796 797 free(instance); 798} 799 800 801 802/* 803 * oncore_poll - called by the transmit procedure 804 */ 805 806static void 807oncore_poll( 808 int unit, 809 struct peer *peer 810 ) 811{ 812 struct instance *instance; 813 814 instance = (struct instance *) peer->procptr->unitptr; 815 if (instance->timeout) { 816 instance->timeout--; 817 if (instance->timeout == 0) { 818 oncore_log(instance, LOG_ERR, 819 "Oncore: No response from @@Cj, shutting down driver"); 820 oncore_shutdown(unit, peer); 821 } else { 822 oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 823 oncore_log(instance, LOG_WARNING, "Oncore: Resend @@Cj"); 824 } 825 return; 826 } 827 828 if (!instance->pollcnt) 829 refclock_report(peer, CEVNT_TIMEOUT); 830 else 831 instance->pollcnt--; 832 peer->procptr->polls++; 833 instance->polled = 1; 834} 835 836 837 838/* 839 * Initialize PPSAPI 840 */ 841 842static int 843oncore_ppsapi( 844 struct instance *instance 845 ) 846{ 847 int cap, mode, mode1; 848 const char *cp; 849 char Msg[160]; 850 851 if (time_pps_getcap(instance->pps_h, &cap) < 0) { 852 oncore_log(instance, LOG_ERR, "time_pps_getcap failed: %m"); 853 return (0); 854 } 855 856 if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) { 857 oncore_log(instance, LOG_ERR, "time_pps_getparams failed: %m"); 858 return (0); 859 } 860 861 /* nb. only turn things on, if someone else has turned something 862 * on before we get here, leave it alone! 863 */ 864 865 if (instance->assert) { 866 cp = "Assert"; 867 mode = PPS_CAPTUREASSERT; 868 mode1 = PPS_OFFSETASSERT; 869 } else { 870 cp = "Clear"; 871 mode = PPS_CAPTURECLEAR; 872 mode1 = PPS_OFFSETCLEAR; 873 } 874 snprintf(Msg, sizeof(Msg), "Initializing timing to %s.", cp); 875 oncore_log(instance, LOG_INFO, Msg); 876 877 if (!(mode & cap)) { 878 snprintf(Msg, sizeof(Msg), 879 "Can't set timing to %s, exiting...", cp); 880 oncore_log(instance, LOG_ERR, Msg); 881 return(0); 882 } 883 884 if (!(mode1 & cap)) { 885 snprintf(Msg, sizeof(Msg), 886 "Can't set %s, this will increase jitter.", cp); 887 oncore_log(instance, LOG_NOTICE, Msg); 888 mode1 = 0; 889 } 890 891 /* only set what is legal */ 892 893 instance->pps_p.mode = (mode | mode1 | PPS_TSFMT_TSPEC) & cap; 894 895 if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) { 896 oncore_log(instance, LOG_ERR, "ONCORE: time_pps_setparams fails"); 897 return(0); /* exit, can't do time_pps_setparans on PPS file */ 898 } 899 900 /* If HARDPPS is on, we tell kernel */ 901 902 if (instance->hardpps) { 903 int i; 904 905 oncore_log(instance, LOG_INFO, "HARDPPS Set."); 906 907 if (instance->assert) 908 i = PPS_CAPTUREASSERT; 909 else 910 i = PPS_CAPTURECLEAR; 911 912 /* we know that 'i' is legal from above */ 913 914 if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i, 915 PPS_TSFMT_TSPEC) < 0) { 916 oncore_log(instance, LOG_ERR, "time_pps_kcbind failed: %m"); 917 oncore_log(instance, LOG_ERR, "HARDPPS failed, abort..."); 918 return (0); 919 } 920 pps_enable = 1; 921 } 922 return(1); 923} 924 925 926 927#ifdef ONCORE_SHMEM_STATUS 928static void 929oncore_init_shmem( 930 struct instance *instance 931 ) 932{ 933 int l, fd; 934 char Msg[160]; 935 u_char *cp, *cp1, *buf, *shmem_old; 936 struct msg_desc *mp; 937 struct stat sbuf; 938 size_t i, n, n1, shmem_length, shmem_old_size; 939 940 /* 941 * The first thing we do is see if there is an instance->shmem_fname file (still) 942 * out there from a previous run. If so, we copy it in and use it to initialize 943 * shmem (so we won't lose our almanac if we need it). 944 */ 945 946 shmem_old = 0; 947 shmem_old_size = 0; 948 if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0) 949 oncore_log(instance, LOG_WARNING, "ONCORE: Can't open SHMEM file"); 950 else { 951 fstat(fd, &sbuf); 952 shmem_old_size = sbuf.st_size; 953 if (shmem_old_size != 0) { 954 shmem_old = emalloc((unsigned) sbuf.st_size); 955 read(fd, shmem_old, shmem_old_size); 956 } 957 close(fd); 958 } 959 960 /* OK, we now create the NEW SHMEM. */ 961 962 if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { 963 oncore_log(instance, LOG_WARNING, "ONCORE: Can't open shmem"); 964 if (shmem_old) 965 free(shmem_old); 966 967 return; 968 } 969 970 /* see how big it needs to be */ 971 972 n = 1; 973 for (mp=oncore_messages; mp->flag[0]; mp++) { 974 mp->shmem = n; 975 /* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */ 976 if (!strcmp(mp->flag, "Cb")) { 977 instance->shmem_Cb = n; 978 n += (mp->len + 3) * 34; 979 } 980 if (!strcmp(mp->flag, "Ba")) { 981 instance->shmem_Ba = n; 982 n += (mp->len + 3) * 3; 983 } 984 if (!strcmp(mp->flag, "Ea")) { 985 instance->shmem_Ea = n; 986 n += (mp->len + 3) * 3; 987 } 988 if (!strcmp(mp->flag, "Ha")) { 989 instance->shmem_Ha = n; 990 n += (mp->len + 3) * 3; 991 } 992 n += (mp->len + 3); 993 } 994 shmem_length = n + 2; 995 996 buf = emalloc(shmem_length); 997 memset(buf, 0, shmem_length); 998 999 /* next build the new SHMEM buffer in memory */ 1000 1001 for (mp=oncore_messages; mp->flag[0]; mp++) { 1002 l = mp->shmem; 1003 buf[l + 0] = mp->len >> 8; 1004 buf[l + 1] = mp->len & 0xff; 1005 buf[l + 2] = 0; 1006 buf[l + 3] = '@'; 1007 buf[l + 4] = '@'; 1008 buf[l + 5] = mp->flag[0]; 1009 buf[l + 6] = mp->flag[1]; 1010 if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) { 1011 if (!strcmp(mp->flag, "Cb")) 1012 n = 35; 1013 else 1014 n = 4; 1015 for (i=1; i<n; i++) { 1016 buf[l + i * (mp->len+3) + 0] = mp->len >> 8; 1017 buf[l + i * (mp->len+3) + 1] = mp->len & 0xff; 1018 buf[l + i * (mp->len+3) + 2] = 0; 1019 buf[l + i * (mp->len+3) + 3] = '@'; 1020 buf[l + i * (mp->len+3) + 4] = '@'; 1021 buf[l + i * (mp->len+3) + 5] = mp->flag[0]; 1022 buf[l + i * (mp->len+3) + 6] = mp->flag[1]; 1023 } 1024 } 1025 } 1026 1027 /* we now walk thru the two buffers (shmem_old and buf, soon to become shmem) 1028 * copying the data in shmem_old to buf. 1029 * When we are done we write it out and free both buffers. 1030 * If the structure sizes dont agree, I will not copy. 1031 * This could be due to an addition/deletion or a problem with the disk file. 1032 */ 1033 1034 if (shmem_old) { 1035 if (shmem_old_size == shmem_length) { 1036 for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3), cp1+=(n+3)) { 1037 n1 = 256*(*(cp1-3)) + *(cp1-2); 1038 if (n == 0 || n1 != n || strncmp((char *) cp, (char *) cp1, 4)) 1039 break; 1040 1041 memcpy(cp, cp1, (size_t) n); 1042 } 1043 } 1044 free(shmem_old); 1045 } 1046 1047 i = write(instance->shmemfd, buf, shmem_length); 1048 free(buf); 1049 1050 if (i != shmem_length) { 1051 oncore_log(instance, LOG_ERR, "ONCORE: error writing shmem"); 1052 close(instance->shmemfd); 1053 return; 1054 } 1055 1056 instance->shmem = (u_char *) mmap(0, shmem_length, 1057 PROT_READ | PROT_WRITE, 1058#ifdef MAP_HASSEMAPHORE 1059 MAP_HASSEMAPHORE | 1060#endif 1061 MAP_SHARED, instance->shmemfd, (off_t)0); 1062 1063 if (instance->shmem == (u_char *)MAP_FAILED) { 1064 instance->shmem = 0; 1065 close(instance->shmemfd); 1066 return; 1067 } 1068 1069 snprintf(Msg, sizeof(Msg), 1070 "SHMEM (size = %ld) is CONFIGURED and available as %s", 1071 (u_long) shmem_length, instance->shmem_fname); 1072 oncore_log(instance, LOG_NOTICE, Msg); 1073} 1074#endif /* ONCORE_SHMEM_STATUS */ 1075 1076 1077 1078/* 1079 * Read Input file if it exists. 1080 */ 1081 1082static void 1083oncore_read_config( 1084 struct instance *instance 1085 ) 1086{ 1087/* 1088 * First we try to open the configuration file 1089 * /etc/ntp.oncore.N 1090 * where N is the unit number viz 127.127.30.N. 1091 * If we don't find it we try 1092 * /etc/ntp.oncoreN 1093 * and then 1094 * /etc/ntp.oncore 1095 * 1096 * If we don't find any then we don't have the cable delay or PPS offset 1097 * and we choose MODE (4) below. 1098 * 1099 * Five Choices for MODE 1100 * (0) ONCORE is preinitialized, don't do anything to change it. 1101 * nb, DON'T set 0D mode, DON'T set Delay, position... 1102 * (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode. 1103 * (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position, 1104 * lock this in, go to 0D mode. 1105 * (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode. 1106 * (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position, 1107 * lock this in, go to 0D mode. 1108 * NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY] 1109 * then this position is set as the INITIAL position of the ONCORE. 1110 * This can reduce the time to first fix. 1111 * ------------------------------------------------------------------------------- 1112 * Note that an Oncore UT without a battery backup retains NO information if it is 1113 * power cycled, with a Battery Backup it remembers the almanac, etc. 1114 * For an Oncore VP, there is an eeprom that will contain this data, along with the 1115 * option of Battery Backup. 1116 * So a UT without Battery Backup is equivalent to doing a HARD RESET on each 1117 * power cycle, since there is nowhere to store the data. 1118 * ------------------------------------------------------------------------------- 1119 * 1120 * If we open one or the other of the files, we read it looking for 1121 * MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS, 1122 * STATUS, POSN3D, POSN2D, CHAN, TRAIM 1123 * then initialize using method MODE. For Mode = (1,3) all of (LAT, LON, HT) must 1124 * be present or mode reverts to (2,4). 1125 * 1126 * Read input file. 1127 * 1128 * # is comment to end of line 1129 * = allowed between 1st and 2nd fields. 1130 * 1131 * Expect to see one line with 'MODE' as first field, followed by an integer 1132 * in the range 0-4 (default = 4). 1133 * 1134 * Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields. 1135 * All numbers are floating point. 1136 * DDD.ddd 1137 * DDD MMM.mmm 1138 * DDD MMM SSS.sss 1139 * 1140 * Expect to see one line with 'HT' as first field, 1141 * followed by 1-2 fields. First is a number, the second is 'FT' or 'M' 1142 * for feet or meters. HT is the height above the GPS ellipsoid. 1143 * If the receiver reports height in both GPS and MSL, then we will report 1144 * the difference GPS-MSL on the clockstats file. 1145 * 1146 * There is an optional line, starting with DELAY, followed 1147 * by 1 or two fields. The first is a number (a time) the second is 1148 * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds. 1149 * DELAY is cable delay, typically a few tens of ns. 1150 * 1151 * There is an optional line, starting with OFFSET, followed 1152 * by 1 or two fields. The first is a number (a time) the second is 1153 * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds. 1154 * OFFSET is the offset of the PPS pulse from 0. (only fully implemented 1155 * with the PPSAPI, we need to be able to tell the Kernel about this 1156 * offset if the Kernel PLL is in use, but can only do this presently 1157 * when using the PPSAPI interface. If not using the Kernel PLL, 1158 * then there is no problem. 1159 * 1160 * There is an optional line, with either ASSERT or CLEAR on it, which 1161 * determine which transition of the PPS signal is used for timing by the 1162 * PPSAPI. If neither is present, then ASSERT is assumed. 1163 * ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input. 1164 * For Flag2, ASSERT=0, and hence is default. 1165 * 1166 * There is an optional line, with HARDPPS on it. Including this line causes 1167 * the PPS signal to control the kernel PLL. 1168 * HARDPPS can also be set with FLAG3 of the ntp.conf input. 1169 * For Flag3, 0 is disabled, and the default. 1170 * 1171 * There are three options that have to do with using the shared memory option. 1172 * First, to enable the option there must be a SHMEM line with a file name. 1173 * The file name is the file associated with the shared memory. 1174 * 1175 * In shared memory, there is one 'record' for each returned variable. 1176 * For the @@Ea data there are three 'records' containing position data. 1177 * There will always be data in the record corresponding to the '0D' @@Ea record, 1178 * and the user has a choice of filling the '3D' record by specifying POSN3D, 1179 * or the '2D' record by specifying POSN2D. In either case the '2D' or '3D' 1180 * record is filled once every 15s. 1181 * 1182 * Two additional variables that can be set are CHAN and TRAIM. These should be 1183 * set correctly by the code examining the @@Cj record, but we bring them out here 1184 * to allow the user to override either the # of channels, or the existence of TRAIM. 1185 * CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be 1186 * followed by YES or NO. 1187 * 1188 * There is an optional line with MASK on it followed by one integer field in the 1189 * range 0 to 89. This sets the satellite mask angle and will determine the minimum 1190 * elevation angle for satellites to be tracked by the receiver. The default value 1191 * is 10 deg for the VP and 0 deg for all other receivers. 1192 * 1193 * So acceptable input would be 1194 * # these are my coordinates (RWC) 1195 * LON -106 34.610 1196 * LAT 35 08.999 1197 * HT 1589 # could equally well say HT 5215 FT 1198 * DELAY 60 ns 1199 */ 1200 1201 FILE *fd; 1202 char *cc, *ca, line[100], units[2], device[64], Msg[160]; 1203 const char *cp; 1204 const char **cpp, *dirs[] = { "/etc/ntp", "/etc", 0 }; 1205 int i, sign, lat_flg, long_flg, ht_flg, mode, mask; 1206 double f1, f2, f3; 1207 1208 fd = NULL; /* just to shutup gcc complaint */ 1209 for (cpp=dirs; *cpp; cpp++) { 1210 cp = *cpp; 1211 snprintf(device, sizeof(device), "%s/ntp.oncore.%d", 1212 cp, instance->unit); /* try "ntp.oncore.0 */ 1213 if ((fd=fopen(device, "r"))) 1214 break; 1215 snprintf(device, sizeof(device), "%s/ntp.oncore%d", 1216 cp, instance->unit); /* try "ntp.oncore0" */ 1217 if ((fd=fopen(device, "r"))) 1218 break; 1219 snprintf(device, sizeof(device), "%s/ntp.oncore", cp); 1220 if ((fd=fopen(device, "r"))) /* last try "ntp.oncore" */ 1221 break; 1222 } 1223 1224 if (!fd) { /* no inputfile, default to the works ... */ 1225 instance->init_type = 4; 1226 return; 1227 } 1228 1229 mode = mask = 0; 1230 lat_flg = long_flg = ht_flg = 0; 1231 while (fgets(line, 100, fd)) { 1232 char *cpw; 1233 1234 /* Remove comments */ 1235 if ((cpw = strchr(line, '#'))) 1236 *cpw = '\0'; 1237 1238 /* Remove trailing space */ 1239 for (i = strlen(line); 1240 i > 0 && isascii((unsigned char)line[i - 1]) && isspace((unsigned char)line[i - 1]); 1241 ) 1242 line[--i] = '\0'; 1243 1244 /* Remove leading space */ 1245 for (cc = line; *cc && isascii((unsigned char)*cc) && isspace((unsigned char)*cc); cc++) 1246 continue; 1247 1248 /* Stop if nothing left */ 1249 if (!*cc) 1250 continue; 1251 1252 /* Uppercase the command and find the arg */ 1253 for (ca = cc; *ca; ca++) { 1254 if (isascii((unsigned char)*ca)) { 1255 if (islower((unsigned char)*ca)) { 1256 *ca = toupper((unsigned char)*ca); 1257 } else if (isspace((unsigned char)*ca) || (*ca == '=')) 1258 break; 1259 } 1260 } 1261 1262 /* Remove space (and possible =) leading the arg */ 1263 for (; *ca && isascii((unsigned char)*ca) && (isspace((unsigned char)*ca) || (*ca == '=')); ca++) 1264 continue; 1265 1266 if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) { 1267 instance->shmem_fname = estrdup(ca); 1268 continue; 1269 } 1270 1271 /* Uppercase argument as well */ 1272 for (cpw = ca; *cpw; cpw++) 1273 if (isascii((unsigned char)*cpw) && islower((unsigned char)*cpw)) 1274 *cpw = toupper((unsigned char)*cpw); 1275 1276 if (!strncmp(cc, "LAT", (size_t) 3)) { 1277 f1 = f2 = f3 = 0; 1278 sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3); 1279 sign = 1; 1280 if (f1 < 0) { 1281 f1 = -f1; 1282 sign = -1; 1283 } 1284 instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/ 1285 lat_flg++; 1286 } else if (!strncmp(cc, "LON", (size_t) 3)) { 1287 f1 = f2 = f3 = 0; 1288 sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3); 1289 sign = 1; 1290 if (f1 < 0) { 1291 f1 = -f1; 1292 sign = -1; 1293 } 1294 instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/ 1295 long_flg++; 1296 } else if (!strncmp(cc, "HT", (size_t) 2)) { 1297 f1 = 0; 1298 units[0] = '\0'; 1299 sscanf(ca, "%lf %1s", &f1, units); 1300 if (units[0] == 'F') 1301 f1 = 0.3048 * f1; 1302 instance->ss_ht = 100 * f1; /* cm */ 1303 ht_flg++; 1304 } else if (!strncmp(cc, "DELAY", (size_t) 5)) { 1305 f1 = 0; 1306 units[0] = '\0'; 1307 sscanf(ca, "%lf %1s", &f1, units); 1308 if (units[0] == 'N') 1309 ; 1310 else if (units[0] == 'U') 1311 f1 = 1000 * f1; 1312 else if (units[0] == 'M') 1313 f1 = 1000000 * f1; 1314 else 1315 f1 = 1000000000 * f1; 1316 if (f1 < 0 || f1 > 1.e9) 1317 f1 = 0; 1318 if (f1 < 0 || f1 > 999999) { 1319 snprintf(Msg, sizeof(Msg), 1320 "PPS Cable delay of %fns out of Range, ignored", 1321 f1); 1322 oncore_log(instance, LOG_WARNING, Msg); 1323 } else 1324 instance->delay = f1; /* delay in ns */ 1325 } else if (!strncmp(cc, "OFFSET", (size_t) 6)) { 1326 f1 = 0; 1327 units[0] = '\0'; 1328 sscanf(ca, "%lf %1s", &f1, units); 1329 if (units[0] == 'N') 1330 ; 1331 else if (units[0] == 'U') 1332 f1 = 1000 * f1; 1333 else if (units[0] == 'M') 1334 f1 = 1000000 * f1; 1335 else 1336 f1 = 1000000000 * f1; 1337 if (f1 < 0 || f1 > 1.e9) 1338 f1 = 0; 1339 if (f1 < 0 || f1 > 999999999.) { 1340 snprintf(Msg, sizeof(Msg), 1341 "PPS Offset of %fns out of Range, ignored", 1342 f1); 1343 oncore_log(instance, LOG_WARNING, Msg); 1344 } else 1345 instance->offset = f1; /* offset in ns */ 1346 } else if (!strncmp(cc, "MODE", (size_t) 4)) { 1347 sscanf(ca, "%d", &mode); 1348 if (mode < 0 || mode > 4) 1349 mode = 4; 1350 } else if (!strncmp(cc, "ASSERT", (size_t) 6)) { 1351 instance->assert = 1; 1352 } else if (!strncmp(cc, "CLEAR", (size_t) 5)) { 1353 instance->assert = 0; 1354 } else if (!strncmp(cc, "HARDPPS", (size_t) 7)) { 1355 instance->hardpps = 1; 1356 } else if (!strncmp(cc, "POSN2D", (size_t) 6)) { 1357 instance->shmem_Posn = 2; 1358 } else if (!strncmp(cc, "POSN3D", (size_t) 6)) { 1359 instance->shmem_Posn = 3; 1360 } else if (!strncmp(cc, "CHAN", (size_t) 4)) { 1361 sscanf(ca, "%d", &i); 1362 if ((i == 6) || (i == 8) || (i == 12)) 1363 instance->chan_in = i; 1364 } else if (!strncmp(cc, "TRAIM", (size_t) 5)) { 1365 instance->traim_in = 1; /* so TRAIM alone is YES */ 1366 if (!strcmp(ca, "NO") || !strcmp(ca, "OFF")) /* Yes/No, On/Off */ 1367 instance->traim_in = 0; 1368 } else if (!strncmp(cc, "MASK", (size_t) 4)) { 1369 sscanf(ca, "%d", &mask); 1370 if (mask > -1 && mask < 90) 1371 instance->Ag = mask; /* Satellite mask angle */ 1372 } 1373 } 1374 fclose(fd); 1375 1376 /* 1377 * OK, have read all of data file, and extracted the good stuff. 1378 * If lat/long/ht specified they ALL must be specified for mode = (1,3). 1379 */ 1380 1381 instance->posn_set = 1; 1382 if (!( lat_flg && long_flg && ht_flg )) { 1383 snprintf(Msg, sizeof(Msg), 1384 "ONCORE: incomplete data on %s", device); 1385 oncore_log (instance, LOG_WARNING, Msg); 1386 instance->posn_set = 0; 1387 if (mode == 1 || mode == 3) { 1388 snprintf(Msg, sizeof(Msg), 1389 "Input Mode = %d, but no/incomplete position, mode set to %d", 1390 mode, mode+1); 1391 oncore_log(instance, LOG_WARNING, Msg); 1392 mode++; 1393 } 1394 } 1395 instance->init_type = mode; 1396 1397 snprintf(Msg, sizeof(Msg), "Input mode = %d", mode); 1398 oncore_log(instance, LOG_INFO, Msg); 1399} 1400 1401 1402 1403/* 1404 * move data from NTP to buffer (toss the extra in the unlikely case it won't fit) 1405 */ 1406 1407static void 1408oncore_receive( 1409 struct recvbuf *rbufp 1410 ) 1411{ 1412 size_t i; 1413 u_char *p; 1414 struct peer *peer; 1415 struct instance *instance; 1416 1417 peer = (struct peer *)rbufp->recv_srcclock; 1418 instance = (struct instance *) peer->procptr->unitptr; 1419 p = (u_char *) &rbufp->recv_space; 1420 1421#ifdef ONCORE_VERBOSE_RECEIVE 1422 if (debug > 4) { 1423 int i; 1424 char Msg[120], Msg2[10]; 1425 1426 snprintf(Msg, sizeof(Msg), ">>> %d bytes available", 1427 rbufp->recv_length); 1428 oncore_log(instance, LOG_DEBUG, Msg); 1429 strncpy(Msg, ">>>", sizeof(Msg)); 1430 for (i = 0; i < rbufp->recv_length; i++) { 1431 snprintf(Msg2, sizeof(Msg2), "%02x ", p[i]); 1432 strncat(Msg, Msg2, sizeof(Msg)); 1433 } 1434 oncore_log(instance, LOG_DEBUG, Msg); 1435 1436 strncpy(Msg, ">>>", sizeof(Msg)); 1437 for (i = 0; i < rbufp->recv_length; i++) { 1438 snprintf(Msg2, sizeof(Msg2), "%03o ", p[i]); 1439 strncat(Msg, Msg2, sizeof(Msg)); 1440 } 1441 oncore_log(instance, LOG_DEBUG, Msg); 1442 } 1443#endif 1444 1445 i = rbufp->recv_length; 1446 if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf]) 1447 i = sizeof(rcvbuf) - rcvptr; /* and some char will be lost */ 1448 memcpy(rcvbuf+rcvptr, p, i); 1449 rcvptr += i; 1450 oncore_consume(instance); 1451} 1452 1453 1454 1455/* 1456 * Deal with any complete messages 1457 */ 1458 1459static void 1460oncore_consume( 1461 struct instance *instance 1462 ) 1463{ 1464 unsigned i, m, l; 1465 1466 while (rcvptr >= 7) { 1467 if (rcvbuf[0] != '@' || rcvbuf[1] != '@') { 1468 /* We're not in sync, lets try to get there */ 1469 for (i=1; i < rcvptr-1; i++) 1470 if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@') 1471 break; 1472#ifdef ONCORE_VERBOSE_CONSUME 1473 if (debug > 4) { 1474 char Msg[120]; 1475 1476 snprintf(Msg, sizeof(Msg), 1477 ">>> skipping %d chars", i); 1478 oncore_log(instance, LOG_DEBUG, Msg); 1479 } 1480#endif 1481 if (i != rcvptr) 1482 memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i)); 1483 rcvptr -= i; 1484 continue; 1485 } 1486 1487 /* Ok, we have a header now */ 1488 l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1; 1489 for(m=0; m<l; m++) 1490 if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2)) 1491 break; 1492 if (m == l) { 1493#ifdef ONCORE_VERBOSE_CONSUME 1494 if (debug > 4) { 1495 char Msg[120]; 1496 1497 snprintf(Msg, sizeof(Msg), 1498 ">>> Unknown MSG, skipping 4 (%c%c)", 1499 rcvbuf[2], rcvbuf[3]); 1500 oncore_log(instance, LOG_DEBUG, Msg); 1501 } 1502#endif 1503 memcpy(rcvbuf, rcvbuf+4, (size_t) 4); 1504 rcvptr -= 4; 1505 continue; 1506 } 1507 1508 l = oncore_messages[m].len; 1509#ifdef ONCORE_VERBOSE_CONSUME 1510 if (debug > 3) { 1511 char Msg[120]; 1512 1513 snprintf(Msg, sizeof(Msg), 1514 "GOT: %c%c %d of %d entry %d", 1515 instance->unit, rcvbuf[2], rcvbuf[3], 1516 rcvptr, l, m); 1517 oncore_log(instance, LOG_DEBUG, Msg); 1518 } 1519#endif 1520 /* Got the entire message ? */ 1521 1522 if (rcvptr < l) 1523 return; 1524 1525 /* are we at the end of message? should be <Cksum><CR><LF> */ 1526 1527 if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') { 1528#ifdef ONCORE_VERBOSE_CONSUME 1529 if (debug) 1530 oncore_log(instance, LOG_DEBUG, "NO <CR><LF> at end of message"); 1531#endif 1532 } else { /* check the CheckSum */ 1533 if (oncore_checksum_ok(rcvbuf, l)) { 1534 if (instance->shmem != NULL) { 1535 instance->shmem[oncore_messages[m].shmem + 2]++; 1536 memcpy(instance->shmem + oncore_messages[m].shmem + 3, 1537 rcvbuf, (size_t) l); 1538 } 1539 oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m); 1540 if (oncore_messages[m].handler) 1541 oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3)); 1542 } 1543#ifdef ONCORE_VERBOSE_CONSUME 1544 else if (debug) { 1545 char Msg[120], Msg2[10]; 1546 1547 oncore_log(instance, LOG_ERR, "Checksum mismatch!"); 1548 snprintf(Msg, sizeof(Msg), "@@%c%c ", rcvbuf[2], rcvbuf[3]); 1549 for (i = 4; i < l; i++) { 1550 snprintf(Msg2, sizeof(Msg2), 1551 "%03o ", rcvbuf[i]); 1552 strncat(Msg, Msg2, sizeof(Msg)); 1553 } 1554 oncore_log(instance, LOG_DEBUG, Msg); 1555 } 1556#endif 1557 } 1558 1559 if (l != rcvptr) 1560 memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l)); 1561 rcvptr -= l; 1562 } 1563} 1564 1565 1566 1567static void 1568oncore_get_timestamp( 1569 struct instance *instance, 1570 long dt1, /* tick offset THIS time step */ 1571 long dt2 /* tick offset NEXT time step */ 1572 ) 1573{ 1574 int Rsm; 1575 u_long j; 1576 l_fp ts, ts_tmp; 1577 double dmy; 1578#ifdef HAVE_STRUCT_TIMESPEC 1579 struct timespec *tsp = 0; 1580#else 1581 struct timeval *tsp = 0; 1582#endif 1583 int current_mode; 1584 pps_params_t current_params; 1585 struct timespec timeout; 1586 pps_info_t pps_i; 1587 char Msg[140]; 1588 1589#if 1 1590 /* If we are in SiteSurvey mode, then we are in 3D mode, and we fall thru. 1591 * If we have Finished the SiteSurvey, then we fall thru for the 14/15 1592 * times we get here in 0D mode (the 1/15 is in 3D for SHMEM). 1593 * This gives good time, which gets better when the SS is done. 1594 */ 1595 1596 if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D)) 1597#else 1598 /* old check, only fall thru for SS_DONE and 0D mode, 2h45m wait for ticks */ 1599 1600 if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D)) 1601#endif 1602 return; 1603 1604 /* Don't do anything without an almanac to define the GPS->UTC delta */ 1605 1606 if (instance->rsm.bad_almanac) 1607 return; 1608 1609 /* Once the Almanac is valid, the M12+T does not produce valid UTC 1610 * immediately. 1611 * Wait for UTC offset decode valid, then wait one message more 1612 * so we are not off by 13 seconds after reset. 1613 */ 1614 1615 if (instance->count5) { 1616 instance->count5--; 1617 return; 1618 } 1619 1620 j = instance->ev_serial; 1621 timeout.tv_sec = 0; 1622 timeout.tv_nsec = 0; 1623 if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i, 1624 &timeout) < 0) { 1625 oncore_log(instance, LOG_ERR, "time_pps_fetch failed"); 1626 return; 1627 } 1628 1629 if (instance->assert) { 1630 tsp = &pps_i.assert_timestamp; 1631 1632#ifdef ONCORE_VERBOSE_GET_TIMESTAMP 1633 if (debug > 2) { 1634 u_long i; 1635 1636 i = (u_long) pps_i.assert_sequence; 1637# ifdef HAVE_STRUCT_TIMESPEC 1638 snprintf(Msg, sizeof(Msg), 1639 "serial/j (%lu, %lu) %ld.%09ld", i, j, 1640 (long)tsp->tv_sec, (long)tsp->tv_nsec); 1641# else 1642 snprintf(Msg, sizeof(Msg), 1643 "serial/j (%lu, %lu) %ld.%06ld", i, j, 1644 (long)tsp->tv_sec, (long)tsp->tv_usec); 1645# endif 1646 oncore_log(instance, LOG_DEBUG, Msg); 1647 } 1648#endif 1649 1650 if (pps_i.assert_sequence == j) { 1651 oncore_log(instance, LOG_NOTICE, "ONCORE: oncore_get_timestamp, error serial pps"); 1652 return; 1653 } 1654 1655 instance->ev_serial = pps_i.assert_sequence; 1656 } else { 1657 tsp = &pps_i.clear_timestamp; 1658 1659#if 0 1660 if (debug > 2) { 1661 u_long i; 1662 1663 i = (u_long) pps_i.clear_sequence; 1664# ifdef HAVE_STRUCT_TIMESPEC 1665 snprintf(Msg, sizeof(Msg), 1666 "serial/j (%lu, %lu) %ld.%09ld", i, j, 1667 (long)tsp->tv_sec, (long)tsp->tv_nsec); 1668# else 1669 snprintf(Msg. sizeof(Msg), 1670 "serial/j (%lu, %lu) %ld.%06ld", i, j, 1671 (long)tsp->tv_sec, (long)tsp->tv_usec); 1672# endif 1673 oncore_log(instance, LOG_DEBUG, Msg); 1674 } 1675#endif 1676 1677 if (pps_i.clear_sequence == j) { 1678 oncore_log(instance, LOG_ERR, "oncore_get_timestamp, error serial pps"); 1679 return; 1680 } 1681 instance->ev_serial = pps_i.clear_sequence; 1682 } 1683 1684 /* convert timespec -> ntp l_fp */ 1685 1686 dmy = tsp->tv_nsec; 1687 dmy /= 1e9; 1688 ts.l_uf = dmy * 4294967296.0; 1689 ts.l_ui = tsp->tv_sec; 1690 1691#if 0 1692 alternate code for previous 4 lines is 1693 dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */ 1694 DTOLFP(dmy, &ts); 1695 dmy = tsp->tv_sec; /* integer part */ 1696 DTOLFP(dmy, &ts_tmp); 1697 L_ADD(&ts, &ts_tmp); 1698 or more simply 1699 dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */ 1700 DTOLFP(dmy, &ts); 1701 ts.l_ui = tsp->tv_sec; 1702#endif /* 0 */ 1703 1704 /* now have timestamp in ts */ 1705 /* add in saw_tooth and offset, these will be ZERO if no TRAIM */ 1706 /* they will be IGNORED if the PPSAPI cant do PPS_OFFSET/ASSERT/CLEAR */ 1707 /* we just try to add them in and dont test for that here */ 1708 1709 /* saw_tooth not really necessary if using TIMEVAL */ 1710 /* since its only precise to us, but do it anyway. */ 1711 1712 /* offset in ns, and is positive (late), we subtract */ 1713 /* to put the PPS time transition back where it belongs */ 1714 1715 /* must hand the offset for the NEXT sec off to the Kernel to do */ 1716 /* the addition, so that the Kernel PLL sees the offset too */ 1717 1718 if (instance->assert) 1719 instance->pps_p.assert_offset.tv_nsec = -dt2; 1720 else 1721 instance->pps_p.clear_offset.tv_nsec = -dt2; 1722 1723 /* The following code is necessary, and not just a time_pps_setparams, 1724 * using the saved instance->pps_p, since some other process on the 1725 * machine may have diddled with the mode bits (say adding something 1726 * that it needs). We take what is there and ADD what we need. 1727 * [[ The results from the time_pps_getcap is unlikely to change so 1728 * we could probably just save it, but I choose to do the call ]] 1729 * Unfortunately, there is only ONE set of mode bits in the kernel per 1730 * interface, and not one set for each open handle. 1731 * 1732 * There is still a race condition here where we might mess up someone 1733 * elses mode, but if he is being careful too, he should survive. 1734 */ 1735 1736 if (time_pps_getcap(instance->pps_h, ¤t_mode) < 0) { 1737 oncore_log(instance, LOG_ERR, "time_pps_getcap failed: %m"); 1738 return; 1739 } 1740 1741 if (time_pps_getparams(instance->pps_h, ¤t_params) < 0) { 1742 oncore_log(instance, LOG_ERR, "time_pps_getparams failed: %m"); 1743 return; 1744 } 1745 1746 /* or current and mine */ 1747 current_params.mode |= instance->pps_p.mode; 1748 /* but only set whats legal */ 1749 current_params.mode &= current_mode; 1750 1751 current_params.assert_offset.tv_sec = 0; 1752 current_params.assert_offset.tv_nsec = -dt2; 1753 current_params.clear_offset.tv_sec = 0; 1754 current_params.clear_offset.tv_nsec = -dt2; 1755 1756 if (time_pps_setparams(instance->pps_h, ¤t_params)) 1757 oncore_log(instance, LOG_ERR, "ONCORE: Error doing time_pps_setparams"); 1758 1759 /* have time from UNIX origin, convert to NTP origin. */ 1760 1761 ts.l_ui += JAN_1970; 1762 instance->pp->lastrec = ts; 1763 1764 /* print out information about this timestamp (long line) */ 1765 1766 ts_tmp = ts; 1767 ts_tmp.l_ui = 0; /* zero integer part */ 1768 LFPTOD(&ts_tmp, dmy); /* convert fractional part to a double */ 1769 j = 1.0e9*dmy; /* then to integer ns */ 1770 1771 Rsm = 0; 1772 if (instance->chan == 6) 1773 Rsm = instance->BEHa[64]; 1774 else if (instance->chan == 8) 1775 Rsm = instance->BEHa[72]; 1776 else if (instance->chan == 12) 1777 Rsm = ((instance->BEHa[129]<<8) | instance->BEHa[130]); 1778 1779 if (instance->chan == 6 || instance->chan == 8) { 1780 char f1[5], f2[5], f3[5], f4[5]; 1781 if (instance->traim) { 1782 snprintf(f1, sizeof(f1), "%d", 1783 instance->BEHn[21]); 1784 snprintf(f2, sizeof(f2), "%d", 1785 instance->BEHn[22]); 1786 snprintf(f3, sizeof(f3), "%2d", 1787 instance->BEHn[23] * 256 + 1788 instance->BEHn[24]); 1789 snprintf(f4, sizeof(f4), "%3d", 1790 (s_char)instance->BEHn[25]); 1791 } else { 1792 strncpy(f1, "x", sizeof(f1)); 1793 strncpy(f2, "x", sizeof(f2)); 1794 strncpy(f3, "xx", sizeof(f3)); 1795 strncpy(f4, "xxx", sizeof(f4)); 1796 } 1797 snprintf(Msg, sizeof(Msg), /* MAX length 128, currently at 127 */ 1798 "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d", 1799 ts.l_ui, j, 1800 instance->pp->year, instance->pp->day, 1801 instance->pp->hour, instance->pp->minute, instance->pp->second, 1802 (long) tsp->tv_sec % 60, 1803 Rsm, 0.1*(256*instance->BEHa[35]+instance->BEHa[36]), 1804 /*rsat dop */ 1805 instance->BEHa[38], instance->BEHa[39], instance->traim, f1, f2, 1806 /* nsat visible, nsat tracked, traim,traim,traim */ 1807 f3, f4, 1808 /* sigma neg-sawtooth */ 1809 /*sat*/ instance->BEHa[41], instance->BEHa[45], instance->BEHa[49], instance->BEHa[53], 1810 instance->BEHa[57], instance->BEHa[61], instance->BEHa[65], instance->BEHa[69] 1811 ); /* will be 0 for 6 chan */ 1812 } else if (instance->chan == 12) { 1813 char f1[5], f2[5], f3[5], f4[5]; 1814 if (instance->traim) { 1815 snprintf(f1, sizeof(f1), "%d", 1816 instance->BEHn[6]); 1817 snprintf(f2, sizeof(f2), "%d", 1818 instance->BEHn[7]); 1819 snprintf(f3, sizeof(f3), "%d", 1820 instance->BEHn[12] * 256 + 1821 instance->BEHn[13]); 1822 snprintf(f4, sizeof(f4), "%3d", 1823 (s_char)instance->BEHn[14]); 1824 } else { 1825 strncpy(f1, "x", sizeof(f1)); 1826 strncpy(f2, "x", sizeof(f2)); 1827 strncpy(f3, "xx", sizeof(f3)); 1828 strncpy(f4, "xxx", sizeof(f4)); 1829 } 1830 snprintf(Msg, sizeof(Msg), 1831 "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d%d%d%d%d", 1832 ts.l_ui, j, 1833 instance->pp->year, instance->pp->day, 1834 instance->pp->hour, instance->pp->minute, instance->pp->second, 1835 (long) tsp->tv_sec % 60, 1836 Rsm, 0.1*(256*instance->BEHa[53]+instance->BEHa[54]), 1837 /*rsat dop */ 1838 instance->BEHa[55], instance->BEHa[56], instance->traim, f1, f2, 1839 /* nsat visible, nsat tracked traim,traim,traim */ 1840 f3, f4, 1841 /* sigma neg-sawtooth */ 1842 /*sat*/ instance->BEHa[58], instance->BEHa[64], instance->BEHa[70], instance->BEHa[76], 1843 instance->BEHa[82], instance->BEHa[88], instance->BEHa[94], instance->BEHa[100], 1844 instance->BEHa[106], instance->BEHa[112], instance->BEHa[118], instance->BEHa[124] 1845 ); 1846 } 1847 1848 /* and some things I dont understand (magic ntp things) */ 1849 1850 if (!refclock_process(instance->pp)) { 1851 refclock_report(instance->peer, CEVNT_BADTIME); 1852 return; 1853 } 1854 1855 oncore_log(instance, LOG_INFO, Msg); /* this is long message above */ 1856 instance->pollcnt = 2; 1857 1858 if (instance->polled) { 1859 instance->polled = 0; 1860 /* instance->pp->dispersion = instance->pp->skew = 0; */ 1861 instance->pp->lastref = instance->pp->lastrec; 1862 refclock_receive(instance->peer); 1863 } 1864} 1865 1866 1867/*************** oncore_msg_XX routines start here *******************/ 1868 1869 1870/* 1871 * print Oncore response message. 1872 */ 1873 1874static void 1875oncore_msg_any( 1876 struct instance *instance, 1877 u_char *buf, 1878 size_t len, 1879 int idx 1880 ) 1881{ 1882#ifdef ONCORE_VERBOSE_MSG_ANY 1883 int i; 1884 const char *fmt = oncore_messages[idx].fmt; 1885 const char *p; 1886 char *q; 1887 char *qlim; 1888#ifdef HAVE_GETCLOCK 1889 struct timespec ts; 1890#endif 1891 struct timeval tv; 1892 char Msg[120], Msg2[10]; 1893 1894 if (debug > 3) { 1895# ifdef HAVE_GETCLOCK 1896 (void) getclock(TIMEOFDAY, &ts); 1897 tv.tv_sec = ts.tv_sec; 1898 tv.tv_usec = ts.tv_nsec / 1000; 1899# else 1900 GETTIMEOFDAY(&tv, 0); 1901# endif 1902 snprintf(Msg, sizeof(Msg), "%ld.%06ld", 1903 (long)tv.tv_sec, (long)tv.tv_usec); 1904 oncore_log(instance, LOG_DEBUG, Msg); 1905 1906 if (!*fmt) { 1907 snprintf(Msg, sizeof(Msg), ">>@@%c%c ", buf[2], 1908 buf[3]); 1909 for(i = 2; i < len && i < 2400 ; i++) { 1910 snprintf(Msg2, sizeof(Msg2), "%02x", 1911 buf[i]); 1912 strncpy(Msg, Msg2, sizeof(Msg)); 1913 1914 } 1915 oncore_log(instance, LOG_DEBUG, Msg); 1916 return; 1917 } else { 1918 strncat(Msg, "##", sizeof(Msg)); 1919 qlim = Msg + sizeof(Msg) - 3; 1920 for (p = fmt, q = Msg + 2; q < qlim && *p; ) { 1921 *q++ = *p++; 1922 *q++ = '_'; 1923 } 1924 *q = '\0'; 1925 oncore_log(instance, LOG_DEBUG, Msg); 1926 snprintf(Msg, sizeof(Msg), "%c%c", buf[2], 1927 buf[3]); 1928 i = 4; 1929 for (p = fmt; *p; p++) { 1930 snprintf(Msg2, "%02x", buf[i++]); 1931 strncat(Msg, Msg2, sizeof(Msg)); 1932 } 1933 oncore_log(instance, LOG_DEBUG, Msg); 1934 } 1935 } 1936#endif 1937} 1938 1939 1940 1941/* Latitude, Longitude, Height */ 1942 1943static void 1944oncore_msg_Adef( 1945 struct instance *instance, 1946 u_char *buf, 1947 size_t len 1948 ) 1949{ 1950} 1951 1952 1953 1954/* Mask Angle */ 1955 1956static void 1957oncore_msg_Ag( 1958 struct instance *instance, 1959 u_char *buf, 1960 size_t len 1961 ) 1962{ char Msg[160]; 1963 const char *cp; 1964 1965 cp = "set to"; 1966 if (instance->o_state == ONCORE_RUN) 1967 cp = "is"; 1968 1969 instance->Ag = buf[4]; 1970 snprintf(Msg, sizeof(Msg), 1971 "Satellite mask angle %s %d degrees", cp, 1972 (int)instance->Ag); 1973 oncore_log(instance, LOG_INFO, Msg); 1974} 1975 1976 1977 1978/* 1979 * get Position hold position 1980 */ 1981 1982static void 1983oncore_msg_As( 1984 struct instance *instance, 1985 u_char *buf, 1986 size_t len 1987 ) 1988{ 1989 instance->ss_lat = buf_w32(&buf[4]); 1990 instance->ss_long = buf_w32(&buf[8]); 1991 instance->ss_ht = buf_w32(&buf[12]); 1992 1993 /* Print out Position */ 1994 oncore_print_posn(instance); 1995} 1996 1997 1998 1999/* 2000 * Try to use Oncore UT+ Auto Survey Feature 2001 * If its not there (VP), set flag to do it ourselves. 2002 */ 2003 2004static void 2005oncore_msg_At( 2006 struct instance *instance, 2007 u_char *buf, 2008 size_t len 2009 ) 2010{ 2011 instance->saw_At = 1; 2012 if (instance->site_survey == ONCORE_SS_TESTING) { 2013 if (buf[4] == 2) { 2014 oncore_log(instance, LOG_NOTICE, 2015 "Initiating hardware 3D site survey"); 2016 2017 oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW"); 2018 instance->site_survey = ONCORE_SS_HW; 2019 } 2020 } 2021} 2022 2023 2024 2025/* 2026 * get PPS Offset 2027 * Nb. @@Ay is not supported for early UT (no plus) model 2028 */ 2029 2030static void 2031oncore_msg_Ay( 2032 struct instance *instance, 2033 u_char *buf, 2034 size_t len 2035 ) 2036{ 2037 char Msg[120]; 2038 2039 if (instance->saw_Ay) 2040 return; 2041 2042 instance->saw_Ay = 1; 2043 2044 instance->offset = buf_w32(&buf[4]); 2045 2046 snprintf(Msg, sizeof(Msg), "PPS Offset is set to %ld ns", 2047 instance->offset); 2048 oncore_log(instance, LOG_INFO, Msg); 2049} 2050 2051 2052 2053/* 2054 * get Cable Delay 2055 */ 2056 2057static void 2058oncore_msg_Az( 2059 struct instance *instance, 2060 u_char *buf, 2061 size_t len 2062 ) 2063{ 2064 char Msg[120]; 2065 2066 if (instance->saw_Az) 2067 return; 2068 2069 instance->saw_Az = 1; 2070 2071 instance->delay = buf_w32(&buf[4]); 2072 2073 snprintf(Msg, sizeof(Msg), "Cable delay is set to %ld ns", 2074 instance->delay); 2075 oncore_log(instance, LOG_INFO, Msg); 2076} 2077 2078 2079 2080/* Ba, Ea and Ha come here, these contain Position */ 2081 2082static void 2083oncore_msg_BaEaHa( 2084 struct instance *instance, 2085 u_char *buf, 2086 size_t len 2087 ) 2088{ 2089 const char *cp; 2090 char Msg[160]; 2091 int mode; 2092 2093 /* OK, we are close to the RUN state now. 2094 * But we have a few more items to initialize first. 2095 * 2096 * At the beginning of this routine there are several 'timers'. 2097 * We enter this routine 1/sec, and since the upper levels of NTP have usurped 2098 * the use of timers, we use the 1/sec entry to do things that 2099 * we would normally do with timers... 2100 */ 2101 2102 if (instance->o_state == ONCORE_CHECK_CHAN) { /* here while checking for the # chan */ 2103 if (buf[2] == 'B') { /* 6chan */ 2104 if (instance->chan_ck < 6) instance->chan_ck = 6; 2105 } else if (buf[2] == 'E') { /* 8chan */ 2106 if (instance->chan_ck < 8) instance->chan_ck = 8; 2107 } else if (buf[2] == 'H') { /* 12chan */ 2108 if (instance->chan_ck < 12) instance->chan_ck = 12; 2109 } 2110 2111 if (instance->count3++ < 5) 2112 return; 2113 2114 instance->count3 = 0; 2115 2116 if (instance->chan_in != -1) /* set in Input */ 2117 instance->chan = instance->chan_in; 2118 else /* set from test */ 2119 instance->chan = instance->chan_ck; 2120 2121 snprintf(Msg, sizeof(Msg), "Input says chan = %d", 2122 instance->chan_in); 2123 oncore_log(instance, LOG_INFO, Msg); 2124 snprintf(Msg, sizeof(Msg), "Model # says chan = %d", 2125 instance->chan_id); 2126 oncore_log(instance, LOG_INFO, Msg); 2127 snprintf(Msg, sizeof(Msg), "Testing says chan = %d", 2128 instance->chan_ck); 2129 oncore_log(instance, LOG_INFO, Msg); 2130 snprintf(Msg, sizeof(Msg), "Using chan = %d", 2131 instance->chan); 2132 oncore_log(instance, LOG_INFO, Msg); 2133 2134 instance->o_state = ONCORE_HAVE_CHAN; 2135 oncore_log(instance, LOG_NOTICE, "state = ONCORE_HAVE_CHAN"); 2136 2137 instance->timeout = 4; 2138 oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 2139 return; 2140 } 2141 2142 if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN) 2143 return; 2144 2145 /* PAUSE 5sec - make sure results are stable, before using position */ 2146 2147 if (instance->count) { 2148 if (instance->count++ < 5) 2149 return; 2150 instance->count = 0; 2151 } 2152 2153 memcpy(instance->BEHa, buf, (size_t) (len+3)); /* Ba, Ea or Ha */ 2154 2155 /* check the antenna (did it get unplugged) and almanac (is it ready) for changes. */ 2156 2157 oncore_check_almanac(instance); 2158 oncore_check_antenna(instance); 2159 2160 /* If we are in Almanac mode, waiting for Almanac, we can't do anything till we have it */ 2161 /* When we have an almanac, we will start the Bn/En/@@Hn messages */ 2162 2163 if (instance->o_state == ONCORE_ALMANAC) 2164 if (oncore_wait_almanac(instance)) 2165 return; 2166 2167 /* do some things once when we get this far in BaEaHa */ 2168 2169 if (instance->once) { 2170 instance->once = 0; 2171 instance->count2 = 1; 2172 2173 /* Have we seen an @@At (position hold) command response */ 2174 /* if not, message out */ 2175 2176 if (instance->chan != 12 && !instance->saw_At) { 2177 oncore_log(instance, LOG_NOTICE, 2178 "Not Good, no @@At command (no Position Hold), must be a GT/GT+"); 2179 oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); 2180 } 2181 2182 /* have an Almanac, can start the SiteSurvey 2183 * (actually only need to get past the almanac_load where we diddle with At 2184 * command,- we can't change it after we start the HW_SS below 2185 */ 2186 2187 mode = instance->init_type; 2188 switch (mode) { 2189 case 0: /* NO initialization, don't change anything */ 2190 case 1: /* Use given Position */ 2191 case 3: 2192 instance->site_survey = ONCORE_SS_DONE; 2193 oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE"); 2194 break; 2195 2196 case 2: 2197 case 4: /* Site Survey */ 2198 oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_TESTING"); 2199 instance->site_survey = ONCORE_SS_TESTING; 2200 instance->count1 = 1; 2201 if (instance->chan == 12) 2202 oncore_sendmsg(instance, oncore_cmd_Gd3, sizeof(oncore_cmd_Gd3)); /* M12+T */ 2203 else 2204 oncore_sendmsg(instance, oncore_cmd_At2, sizeof(oncore_cmd_At2)); /* not GT, arg not VP */ 2205 break; 2206 } 2207 2208 /* Read back PPS Offset for Output */ 2209 /* Nb. This will fail silently for early UT (no plus) and M12 models */ 2210 2211 oncore_sendmsg(instance, oncore_cmd_Ayx, sizeof(oncore_cmd_Ayx)); 2212 2213 /* Read back Cable Delay for Output */ 2214 2215 oncore_sendmsg(instance, oncore_cmd_Azx, sizeof(oncore_cmd_Azx)); 2216 2217 /* Read back Satellite Mask Angle for Output */ 2218 2219 oncore_sendmsg(instance, oncore_cmd_Agx, sizeof(oncore_cmd_Agx)); 2220 } 2221 2222 2223 /* Unfortunately, the Gd3 command returns '3' for the M12 v1.3 firmware where it is 2224 * out-of-range and it should return 0-2. (v1.3 can't do a HW Site Survey) 2225 * We must do the Gd3, and then wait a cycle or two for things to settle, 2226 * then check Ha[130]&0x10 to see if a SS is in progress. 2227 * We will set SW if HW has not been set after an appropriate delay. 2228 */ 2229 2230 if (instance->site_survey == ONCORE_SS_TESTING) { 2231 if (instance->chan == 12) { 2232 if (instance->count1) { 2233 if (instance->count1++ > 5 || instance->BEHa[130]&0x10) { 2234 instance->count1 = 0; 2235 if (instance->BEHa[130]&0x10) { 2236 oncore_log(instance, LOG_NOTICE, 2237 "Initiating hardware 3D site survey"); 2238 2239 oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW"); 2240 instance->site_survey = ONCORE_SS_HW; 2241 } else { 2242 oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_SW"); 2243 instance->site_survey = ONCORE_SS_SW; 2244 } 2245 } 2246 } 2247 } else { 2248 if (instance->count1) { 2249 if (instance->count1++ > 5) { 2250 instance->count1 = 0; 2251 /* 2252 * For instance->site_survey to still be ONCORE_SS_TESTING, then after a 5sec 2253 * wait after the @@At2/@@Gd3 command we have not changed the state to 2254 * ONCORE_SS_HW. If the Hardware is capable of doing a Site Survey, then 2255 * the variable would have been changed by now. 2256 * There are three possibilities: 2257 * 6/8chan 2258 * (a) We did not get a response to the @@At0 or @@At2 commands, 2259 * and it must be a GT/GT+/SL with no position hold mode. 2260 * We will have to do it ourselves. 2261 * (b) We saw the @@At0, @@At2 commands, but @@At2 failed, 2262 * must be a VP or older UT which doesn't have Site Survey mode. 2263 * We will have to do it ourselves. 2264 * 12chan 2265 * (c) We saw the @@Gd command, and saw H[13]*0x10 2266 * We will have to do it ourselves (done above) 2267 */ 2268 2269 snprintf(Msg, sizeof(Msg), 2270 "Initiating software 3D site survey (%d samples)", 2271 POS_HOLD_AVERAGE); 2272 oncore_log(instance, LOG_INFO, Msg); 2273 2274 oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_SW"); 2275 instance->site_survey = ONCORE_SS_SW; 2276 2277 instance->ss_lat = instance->ss_long = instance->ss_ht = 0; 2278 if (instance->chan == 12) 2279 oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */ 2280 else { 2281 oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */ 2282 oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* disable */ 2283 } 2284 } 2285 } 2286 } 2287 } 2288 2289 /* check the mode we are in 0/2/3D */ 2290 2291 if (instance->chan == 6) { 2292 if (instance->BEHa[64]&0x8) 2293 instance->mode = MODE_0D; 2294 else if (instance->BEHa[64]&0x10) 2295 instance->mode = MODE_2D; 2296 else if (instance->BEHa[64]&0x20) 2297 instance->mode = MODE_3D; 2298 } else if (instance->chan == 8) { 2299 if (instance->BEHa[72]&0x8) 2300 instance->mode = MODE_0D; 2301 else if (instance->BEHa[72]&0x10) 2302 instance->mode = MODE_2D; 2303 else if (instance->BEHa[72]&0x20) 2304 instance->mode = MODE_3D; 2305 } else if (instance->chan == 12) { 2306 int bits; 2307 2308 bits = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */ 2309 if (bits == 0x4) 2310 instance->mode = MODE_0D; 2311 else if (bits == 0x6) 2312 instance->mode = MODE_2D; 2313 else if (bits == 0x7) 2314 instance->mode = MODE_3D; 2315 } 2316 2317 /* copy the record to the (extra) location in SHMEM */ 2318 2319 if (instance->shmem) { 2320 int i; 2321 u_char *smp; /* pointer to start of shared mem for Ba/Ea/Ha */ 2322 2323 switch(instance->chan) { 2324 case 6: smp = &instance->shmem[instance->shmem_Ba]; break; 2325 case 8: smp = &instance->shmem[instance->shmem_Ea]; break; 2326 case 12: smp = &instance->shmem[instance->shmem_Ha]; break; 2327 default: smp = (u_char *) NULL; break; 2328 } 2329 2330 switch (instance->mode) { 2331 case MODE_0D: i = 1; break; /* 0D, Position Hold */ 2332 case MODE_2D: i = 2; break; /* 2D, Altitude Hold */ 2333 case MODE_3D: i = 3; break; /* 3D fix */ 2334 default: i = 0; break; 2335 } 2336 2337 if (i && smp != NULL) { 2338 i *= (len+6); 2339 smp[i + 2]++; 2340 memcpy(&smp[i+3], buf, (size_t) (len+3)); 2341 } 2342 } 2343 2344 /* 2345 * check if traim timer active 2346 * if it hasn't been cleared, then @@Bn/@@En/@@Hn did not respond 2347 */ 2348 2349 if (instance->traim_delay) { 2350 if (instance->traim_delay++ > 5) { 2351 instance->traim = 0; 2352 instance->traim_delay = 0; 2353 cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF"; 2354 oncore_log(instance, LOG_INFO, cp); 2355 2356 oncore_set_traim(instance); 2357 } else 2358 return; 2359 2360 } 2361 2362 /* by now should have a @@Ba/@@Ea/@@Ha with good data in it */ 2363 2364 if (!instance->have_dH && !instance->traim_delay) 2365 oncore_compute_dH(instance); 2366 2367 /* 2368 * must be ONCORE_RUN if we are here. 2369 * Have # chan and TRAIM by now. 2370 */ 2371 2372 instance->pp->year = buf[6]*256+buf[7]; 2373 instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]); 2374 instance->pp->hour = buf[8]; 2375 instance->pp->minute = buf[9]; 2376 instance->pp->second = buf[10]; 2377 2378 /* 2379 * Are we doing a Hardware or Software Site Survey? 2380 */ 2381 2382 if (instance->site_survey == ONCORE_SS_HW || instance->site_survey == ONCORE_SS_SW) 2383 oncore_ss(instance); 2384 2385 /* see if we ever saw a response from the @@Ayx above */ 2386 2387 if (instance->count2) { 2388 if (instance->count2++ > 5) { /* this delay to check on @@Ay command */ 2389 instance->count2 = 0; 2390 2391 /* Have we seen an Ay (1PPS time offset) command response */ 2392 /* if not, and non-zero offset, zero the offset, and send message */ 2393 2394 if (!instance->saw_Ay && instance->offset) { 2395 oncore_log(instance, LOG_INFO, "No @@Ay command, PPS OFFSET ignored"); 2396 instance->offset = 0; 2397 } 2398 } 2399 } 2400 2401 /* 2402 * Check the leap second status once per day. 2403 */ 2404 2405 oncore_check_leap_sec(instance); 2406 2407 /* 2408 * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn. 2409 */ 2410 2411 if (instance->shmem && !instance->shmem_bad_Ea && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE)) 2412 oncore_shmem_get_3D(instance); 2413 2414 if (!instance->traim) /* NO traim, no BnEnHn, go get tick */ 2415 oncore_get_timestamp(instance, instance->offset, instance->offset); 2416} 2417 2418 2419 2420/* Almanac Status */ 2421 2422static void 2423oncore_msg_Bd( 2424 struct instance *instance, 2425 u_char *buf, 2426 size_t len 2427 ) 2428{ 2429 char Msg[160]; 2430 2431 snprintf(Msg, sizeof(Msg), 2432 "Bd: Almanac %s, week = %d, t = %d, %d SVs: %x", 2433 ((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6], 2434 buf[7], w32(&buf[8])); 2435 oncore_log(instance, LOG_NOTICE, Msg); 2436} 2437 2438 2439 2440/* get leap-second warning message */ 2441 2442/* 2443 * @@Bj does NOT behave as documented in current Oncore firmware. 2444 * It turns on the LEAP indicator when the data is set, and does not, 2445 * as documented, wait until the beginning of the month when the 2446 * leap second will occur. 2447 * Since this firmware bug will never be fixed in all the outstanding Oncore receivers 2448 * @@Bj is only called in June/December. 2449 */ 2450 2451static void 2452oncore_msg_Bj( 2453 struct instance *instance, 2454 u_char *buf, 2455 size_t len 2456 ) 2457{ 2458 const char *cp; 2459 2460 instance->saw_Bj = 1; 2461 2462 switch(buf[4]) { 2463 case 1: 2464 instance->pp->leap = LEAP_ADDSECOND; 2465 cp = "Set pp.leap to LEAP_ADDSECOND"; 2466 break; 2467 case 2: 2468 instance->pp->leap = LEAP_DELSECOND; 2469 cp = "Set pp.leap to LEAP_DELSECOND"; 2470 break; 2471 case 0: 2472 default: 2473 instance->pp->leap = LEAP_NOWARNING; 2474 cp = "Set pp.leap to LEAP_NOWARNING"; 2475 break; 2476 } 2477 oncore_log(instance, LOG_NOTICE, cp); 2478} 2479 2480 2481 2482static void 2483oncore_msg_Bl( 2484 struct instance *instance, 2485 u_char *buf, 2486 size_t len 2487 ) 2488{ 2489 int chan, id, subframe, valid, page, i, j, tow; 2490 int day_now, day_lsf; 2491 const char *cp = NULL /* XXX gcc */; 2492 char Msg[120]; 2493 enum { 2494 WARN_NOT_YET, 2495 WARN_0, 2496 WARN_PLUS, 2497 WARN_MINUS 2498 } warn; 2499 2500 day_now = day_lsf = 0; 2501 2502 chan = buf[4] & 0377; 2503 id = buf[5] & 0377; 2504 subframe = buf[6] & 017; 2505 valid = (buf[6] >> 4) & 017; 2506 page = buf[7]; 2507 2508 if ((!instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 4 && page == 18 && valid == 10)) { 2509 instance->Bl.dt_ls = buf[32]; 2510 instance->Bl.WN_lsf = buf[33]; 2511 instance->Bl.DN_lsf = buf[34]; 2512 instance->Bl.dt_lsf = buf[35]; 2513 instance->Bl.lsf_flg++; 2514 } 2515 if ((instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 1 && valid == 10)) { 2516 i = (buf[7+7]<<8) + buf[7+8]; 2517 instance->Bl.WN = i >> 6; 2518 tow = (buf[7+4]<<16) + (buf[7+5]<<8) + buf[7+6]; 2519 tow >>= 7; 2520 tow = tow & 0377777; 2521 tow <<= 2; 2522 instance->Bl.DN = tow/57600L + 1; 2523 instance->Bl.wn_flg++; 2524 } 2525 if (instance->Bl.wn_flg && instance->Bl.lsf_flg) { 2526 instance->Bl.wn_flg = instance->Bl.lsf_flg = 0; 2527 oncore_cmd_Bl[2] = 0; 2528 oncore_sendmsg(instance, oncore_cmd_Bl, sizeof oncore_cmd_Bl); 2529 oncore_cmd_Bl[2] = 1; 2530 2531 i = instance->Bl.WN&01400; 2532 instance->Bl.WN_lsf |= i; 2533 2534 /* have everything I need, doit */ 2535 2536 i = (instance->Bl.WN_lsf - instance->Bl.WN); 2537 if (i < 0) 2538 i += 1024; 2539 day_now = instance->Bl.DN; 2540 day_lsf = 7*i + instance->Bl.DN_lsf; 2541 2542 /* ignore if in past or more than a month in future */ 2543 2544 warn = WARN_NOT_YET; 2545 if (day_lsf >= day_now && day_lsf - day_now < 32) { 2546 /* if < 28d, doit, if 28-31, ck day-of-month < 20 (not at end of prev month) */ 2547 if (day_lsf - day_now < 28 || instance->BEHa[5] < 20) { 2548 i = instance->Bl.dt_lsf - instance->Bl.dt_ls; 2549 switch (i) { 2550 case -1: 2551 warn = WARN_MINUS; 2552 break; 2553 case 0: 2554 warn = WARN_0; 2555 break; 2556 case 1: 2557 warn = WARN_PLUS; 2558 break; 2559 } 2560 } 2561 } 2562 2563 switch (warn) { 2564 case WARN_0: 2565 case WARN_NOT_YET: 2566 instance->peer->leap = LEAP_NOWARNING; 2567 cp = "Set peer.leap to LEAP_NOWARNING"; 2568 break; 2569 case WARN_MINUS: 2570 instance->peer->leap = LEAP_DELSECOND; 2571 cp = "Set peer.leap to LEAP_DELSECOND"; 2572 break; 2573 case WARN_PLUS: 2574 instance->peer->leap = LEAP_ADDSECOND; 2575 cp = "Set peer.leap to LEAP_ADDSECOND"; 2576 break; 2577 } 2578 oncore_log(instance, LOG_NOTICE, cp); 2579 2580 i = instance->Bl.dt_lsf-instance->Bl.dt_ls; 2581 if (i) { 2582 j = (i >= 0) ? i : -i; /* abs(i) */ 2583 snprintf(Msg, sizeof(Msg), 2584 "see Leap_Second (%c%d) in %d days", 2585 ((i >= 0) ? '+' : '-'), j, 2586 day_lsf-day_now); 2587 oncore_log(instance, LOG_NOTICE, Msg); 2588 } 2589 } 2590 snprintf(Msg, sizeof(Msg), 2591 "dt_ls = %d dt_lsf = %d WN = %d DN = %d WN_lsf = %d DNlsf = %d wn_flg = %d lsf_flg = %d Bl_day = %d", 2592 instance->Bl.dt_ls, instance->Bl.dt_lsf, 2593 instance->Bl.WN, instance->Bl.DN, 2594 instance->Bl.WN_lsf, instance->Bl.DN_lsf, 2595 instance->Bl.wn_flg, instance->Bl.lsf_flg, 2596 instance->Bl.Bl_day); 2597 oncore_log(instance, LOG_INFO, Msg); 2598} 2599 2600 2601static void 2602oncore_msg_BnEnHn( 2603 struct instance *instance, 2604 u_char *buf, 2605 size_t len 2606 ) 2607{ 2608 long dt1, dt2; 2609 2610 if (instance->o_state != ONCORE_RUN) 2611 return; 2612 2613 if (instance->traim_delay) { /* flag that @@Bn/@@En/Hn returned */ 2614 instance->traim_ck = 1; 2615 instance->traim_delay = 0; 2616 oncore_log(instance, LOG_NOTICE, "ONCORE: Detected TRAIM, TRAIM = ON"); 2617 2618 oncore_set_traim(instance); 2619 } 2620 2621 memcpy(instance->BEHn, buf, (size_t) len); /* Bn or En or Hn */ 2622 2623 if (!instance->traim) /* BnEnHn will be turned off in any case */ 2624 return; 2625 2626 /* If Time RAIM doesn't like it, don't trust it */ 2627 2628 if (buf[2] == 'H') { 2629 if (instance->BEHn[6]) { /* bad TRAIM */ 2630 oncore_log(instance, LOG_WARNING, "BAD TRAIM"); 2631 return; 2632 } 2633 2634 dt1 = instance->saw_tooth + instance->offset; /* dt this time step */ 2635 instance->saw_tooth = (s_char) instance->BEHn[14]; /* update for next time Hn[14] */ 2636 dt2 = instance->saw_tooth + instance->offset; /* dt next time step */ 2637 } else { 2638 if (instance->BEHn[21]) /* bad TRAIM */ 2639 return; 2640 2641 dt1 = instance->saw_tooth + instance->offset; /* dt this time step */ 2642 instance->saw_tooth = (s_char) instance->BEHn[25]; /* update for next time Bn[25], En[25] */ 2643 dt2 = instance->saw_tooth + instance->offset; /* dt next time step */ 2644 } 2645 2646 oncore_get_timestamp(instance, dt1, dt2); 2647} 2648 2649 2650 2651/* Here for @@Ca, @@Fa and @@Ia messages */ 2652 2653/* These are Self test Commands for 6, 8, and 12 chan receivers. 2654 * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE. 2655 * It was found that under some circumstances the following 2656 * command would fail if issued immediately after the return from the 2657 * @@Fa, but a 2sec delay seemed to fix things. Since simply calling 2658 * sleep(2) is wasteful, and may cause trouble for some OS's, repeating 2659 * itimer, we set a flag, and test it at the next POLL. If it hasn't 2660 * been cleared, we reissue the @@Cj that is issued below. 2661 * Note that we do a @@Cj at the beginning, and again here. 2662 * The first is to get the info, the 2nd is just used as a safe command 2663 * after the @@Fa for all Oncores (and it was in this posn in the 2664 * original code). 2665 */ 2666 2667static void 2668oncore_msg_CaFaIa( 2669 struct instance *instance, 2670 u_char *buf, 2671 size_t len 2672 ) 2673{ 2674 char Msg[120]; 2675 int i; 2676 2677 if (instance->o_state == ONCORE_TEST_SENT) { 2678 enum antenna_state antenna; 2679 2680 instance->timeout = 0; 2681 2682#if ONCORE_VERBOSE_SELF_TEST 2683 if (debug > 2) { 2684 if (buf[2] == 'I') 2685 snprintf(Msg, sizeof(Msg), 2686 ">>@@%ca %x %x %x", buf[2], 2687 buf[4], buf[5], buf[6]); 2688 else 2689 snprintf(Msg, sizeof(Msg), 2690 ">>@@%ca %x %x", buf[2], 2691 buf[4], buf[5]); 2692 oncore_log(instance, LOG_DEBUG, Msg); 2693 } 2694#endif 2695 2696 antenna = (buf[4] & 0xc0) >> 6; 2697 buf[4] &= ~0xc0; 2698 2699 i = buf[4] || buf[5]; 2700 if (buf[2] == 'I') i = i || buf[6]; 2701 if (i) { 2702 if (buf[2] == 'I') 2703 snprintf(Msg, sizeof(Msg), 2704 "self test failed: result %02x %02x %02x", 2705 buf[4], buf[5], buf[6]); 2706 else 2707 snprintf(Msg, sizeof(Msg), 2708 "self test failed: result %02x %02x", 2709 buf[4], buf[5]); 2710 oncore_log(instance, LOG_ERR, Msg); 2711 2712 oncore_log(instance, LOG_ERR, 2713 "ONCORE: self test failed, shutting down driver"); 2714 2715 refclock_report(instance->peer, CEVNT_FAULT); 2716 oncore_shutdown(instance->unit, instance->peer); 2717 return; 2718 } 2719 2720 /* report the current antenna state */ 2721 2722 oncore_antenna_report(instance, antenna); 2723 2724 instance->o_state = ONCORE_INIT; 2725 oncore_log(instance, LOG_NOTICE, "state = ONCORE_INIT"); 2726 2727 instance->timeout = 4; 2728 oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 2729 } 2730} 2731 2732 2733 2734/* 2735 * Demultiplex the almanac into shmem 2736 */ 2737 2738static void 2739oncore_msg_Cb( 2740 struct instance *instance, 2741 u_char *buf, 2742 size_t len 2743 ) 2744{ 2745 int i; 2746 2747 if (instance->shmem == NULL) 2748 return; 2749 2750 if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26) 2751 i = buf[5]; 2752 else if (buf[4] == 4 && buf[5] <= 5) 2753 i = buf[5] + 24; 2754 else if (buf[4] == 4 && buf[5] <= 10) 2755 i = buf[5] + 23; 2756 else if (buf[4] == 4 && buf[5] == 25) 2757 i = 34; 2758 else { 2759 oncore_log(instance, LOG_NOTICE, "Cb: Response is NO ALMANAC"); 2760 return; 2761 } 2762 2763 i *= 36; 2764 instance->shmem[instance->shmem_Cb + i + 2]++; 2765 memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3)); 2766 2767#ifdef ONCORE_VERBOSE_MSG_CB 2768 { 2769 char Msg[160]; 2770 2771 snprintf(Msg, sizeof(Msg), "See Cb [%d,%d]", buf[4], 2772 buf[5]); 2773 oncore_log(instance, LOG_DEBUG, Msg); 2774 } 2775#endif 2776} 2777 2778 2779 2780/* 2781 * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup 2782 * not so for VP (eeprom) or any unit with a battery 2783 */ 2784 2785static void 2786oncore_msg_Cf( 2787 struct instance *instance, 2788 u_char *buf, 2789 size_t len 2790 ) 2791{ 2792 if (instance->o_state == ONCORE_RESET_SENT) { 2793 oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */ 2794 /* Reset set VP to IDLE */ 2795 instance->o_state = ONCORE_TEST_SENT; 2796 oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT"); 2797 2798 oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 2799 } 2800} 2801 2802 2803 2804/* 2805 * This is the Grand Central Station for the Preliminary Initialization. 2806 * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running. 2807 * 2808 * We do an @@Cj whenever we need a safe command for all Oncores. 2809 * The @@Cj gets us back here where we can switch to the next phase of setup. 2810 * 2811 * o Once at the very beginning (in start) to get the Model number. 2812 * This info is printed, but no longer used. 2813 * o Again after we have determined the number of Channels in the receiver. 2814 * o And once later after we have done a reset and test, (which may hang), 2815 * as we are about to initialize the Oncore and start it running. 2816 * o We have one routine below for each case. 2817 */ 2818 2819static void 2820oncore_msg_Cj( 2821 struct instance *instance, 2822 u_char *buf, 2823 size_t len 2824 ) 2825{ 2826 int mode; 2827 2828 memcpy(instance->Cj, buf, len); 2829 2830 instance->timeout = 0; 2831 if (instance->o_state == ONCORE_CHECK_ID) { 2832 oncore_msg_Cj_id(instance, buf, len); 2833 oncore_chan_test(instance); 2834 } else if (instance->o_state == ONCORE_HAVE_CHAN) { 2835 mode = instance->init_type; 2836 if (mode == 3 || mode == 4) { /* Cf will return here to check for TEST */ 2837 instance->o_state = ONCORE_RESET_SENT; 2838 oncore_log(instance, LOG_NOTICE, "state = ONCORE_RESET_SENT"); 2839 oncore_sendmsg(instance, oncore_cmd_Cf, sizeof(oncore_cmd_Cf)); 2840 } else { 2841 instance->o_state = ONCORE_TEST_SENT; 2842 oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT"); 2843 } 2844 } 2845 2846 if (instance->o_state == ONCORE_TEST_SENT) { 2847 if (instance->chan == 6) 2848 oncore_sendmsg(instance, oncore_cmd_Ca, sizeof(oncore_cmd_Ca)); 2849 else if (instance->chan == 8) 2850 oncore_sendmsg(instance, oncore_cmd_Fa, sizeof(oncore_cmd_Fa)); 2851 else if (instance->chan == 12) 2852 oncore_sendmsg(instance, oncore_cmd_Ia, sizeof(oncore_cmd_Ia)); 2853 } else if (instance->o_state == ONCORE_INIT) 2854 oncore_msg_Cj_init(instance, buf, len); 2855} 2856 2857 2858 2859/* The information on determining a Oncore 'Model', viz VP, UT, etc, from 2860 * the Model Number comes from "Richard M. Hambly" <rick@cnssys.com> 2861 * and from Motorola. Until recently Rick was the only source of 2862 * this information as Motorola didn't give the information out. 2863 * 2864 * Determine the Type from the Model #, this determines #chan and if TRAIM is 2865 * available. 2866 * 2867 * The Information from this routine is NO LONGER USED. 2868 * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED 2869 */ 2870 2871static void 2872oncore_msg_Cj_id( 2873 struct instance *instance, 2874 u_char *buf, 2875 size_t len 2876 ) 2877{ 2878 const char *cp, *cp1; 2879 char *cp2, Model[21], Msg[160]; 2880 2881 /* Write Receiver ID message to clockstats file */ 2882 2883 instance->Cj[294] = '\0'; 2884 for (cp= (char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) { 2885 char *cpw = strchr(cp, '\r'); 2886 if (!cpw) 2887 cpw = (char *)&instance->Cj[294]; 2888 *cpw = '\0'; 2889 oncore_log(instance, LOG_NOTICE, cp); 2890 *cpw = '\r'; 2891 cp = cpw+2; 2892 } 2893 2894 /* next, the Firmware Version and Revision numbers */ 2895 2896 instance->version = atoi((char *) &instance->Cj[83]); 2897 instance->revision = atoi((char *) &instance->Cj[111]); 2898 2899 /* from model number decide which Oncore this is, 2900 and then the number of channels */ 2901 2902 for (cp= (char *) &instance->Cj[160]; *cp == ' '; cp++) /* start right after 'Model #' */ 2903 ; 2904 cp1 = cp; 2905 cp2 = Model; 2906 for (; !isspace((unsigned char)*cp) && cp-cp1 < 20; cp++, cp2++) 2907 *cp2 = *cp; 2908 *cp2 = '\0'; 2909 2910 cp = 0; 2911 if (!strncmp(Model, "PVT6", (size_t) 4)) { 2912 cp = "PVT6"; 2913 instance->model = ONCORE_PVT6; 2914 } else if (Model[0] == 'A') { 2915 cp = "Basic"; 2916 instance->model = ONCORE_BASIC; 2917 } else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) { 2918 cp = "VP"; 2919 instance->model = ONCORE_VP; 2920 } else if (Model[0] == 'P') { 2921 cp = "M12"; 2922 instance->model = ONCORE_M12; 2923 } else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') { 2924 if (Model[5] == 'N') { 2925 cp = "GT"; 2926 instance->model = ONCORE_GT; 2927 } else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') { 2928 cp = "GT+"; 2929 instance->model = ONCORE_GTPLUS; 2930 } else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) { 2931 cp = "UT"; 2932 instance->model = ONCORE_UT; 2933 } else if (Model[1] == '5' && Model[5] == 'G') { 2934 cp = "UT+"; 2935 instance->model = ONCORE_UTPLUS; 2936 } else if (Model[1] == '6' && Model[5] == 'G') { 2937 cp = "SL"; 2938 instance->model = ONCORE_SL; 2939 } else { 2940 cp = "Unknown"; 2941 instance->model = ONCORE_UNKNOWN; 2942 } 2943 } else { 2944 cp = "Unknown"; 2945 instance->model = ONCORE_UNKNOWN; 2946 } 2947 2948 /* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */ 2949 2950 snprintf(Msg, sizeof(Msg), 2951 "This looks like an Oncore %s with version %d.%d firmware.", 2952 cp, instance->version, instance->revision); 2953 oncore_log(instance, LOG_INFO, Msg); 2954 2955 instance->chan_id = 8; /* default */ 2956 if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6) 2957 instance->chan_id = 6; 2958 else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS) 2959 instance->chan_id = 8; 2960 else if (instance->model == ONCORE_M12) 2961 instance->chan_id = 12; 2962 2963 instance->traim_id = 0; /* default */ 2964 if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6) 2965 instance->traim_id = 0; 2966 else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS) 2967 instance->traim_id = 1; 2968 else if (instance->model == ONCORE_M12) 2969 instance->traim_id = -1; 2970 2971 snprintf(Msg, sizeof(Msg), "Channels = %d, TRAIM = %s", 2972 instance->chan_id, 2973 ((instance->traim_id < 0) 2974 ? "UNKNOWN" 2975 : (instance->traim_id > 0) 2976 ? "ON" 2977 : "OFF")); 2978 oncore_log(instance, LOG_INFO, Msg); 2979} 2980 2981 2982 2983/* OK, know type of Oncore, have possibly reset it, and have tested it. 2984 * We know the number of channels. 2985 * We will determine whether we have TRAIM before we actually start. 2986 * Now initialize. 2987 */ 2988 2989static void 2990oncore_msg_Cj_init( 2991 struct instance *instance, 2992 u_char *buf, 2993 size_t len 2994 ) 2995{ 2996 char Msg[160]; 2997 u_char Cmd[20]; 2998 int mode; 2999 3000 3001 /* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to 3002 * start again if we go from 0D -> 3D, then loses them again when we 3003 * go from 3D -> 0D. We do this to get a @@Ea message for SHMEM. 3004 * For NOW we will turn this aspect of filling SHMEM off for the M12 3005 */ 3006 3007 if (instance->chan == 12) { 3008 instance->shmem_bad_Ea = 1; 3009 snprintf(Msg, sizeof(Msg), 3010 "*** SHMEM partially enabled for ONCORE M12 s/w v%d.%d ***", 3011 instance->version, instance->revision); 3012 oncore_log(instance, LOG_NOTICE, Msg); 3013 } 3014 3015 oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */ 3016 oncore_sendmsg(instance, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */ 3017 oncore_sendmsg(instance, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */ 3018 oncore_sendmsg(instance, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */ 3019 oncore_sendmsg(instance, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */ 3020 oncore_sendmsg(instance, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */ 3021 oncore_sendmsg(instance, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */ 3022 3023 mode = instance->init_type; 3024 3025 /* If there is Position input in the Config file 3026 * and mode = (1,3) set it as posn hold posn, goto 0D mode. 3027 * or mode = (2,4) set it as INITIAL position, and do Site Survey. 3028 */ 3029 3030 if (instance->posn_set) { 3031 oncore_log(instance, LOG_INFO, "Setting Posn from input data"); 3032 oncore_set_posn(instance); /* this should print posn indirectly thru the As cmd */ 3033 } else /* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */ 3034 if (instance->chan != 12) 3035 oncore_sendmsg(instance, oncore_cmd_Atx, sizeof(oncore_cmd_Atx)); 3036 3037 if (mode != 0) { 3038 /* cable delay in ns */ 3039 memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az)); 3040 w32_buf(&Cmd[-2+4], (int)instance->delay); 3041 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Az)); /* 6,8,12 */ 3042 3043 /* PPS offset in ns */ 3044 if (instance->offset) { 3045 memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay)); /* some have it, some don't */ 3046 w32_buf(&Cmd[-2+4], instance->offset); /* will check for hw response */ 3047 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ay)); 3048 } 3049 3050 /* Satellite mask angle */ 3051 3052 if (instance->Ag != 0xff) { /* will have 0xff in it if not set by user */ 3053 memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag)); 3054 Cmd[-2+4] = instance->Ag; 3055 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ag)); 3056 } 3057 } 3058 3059 /* 6, 8 12 chan - Position/Status/Data Output Message, 1/s 3060 * now we're really running 3061 * these were ALL started in the chan test, 3062 * However, if we had mode=3,4 then commands got turned off, so we turn 3063 * them on again here just in case 3064 */ 3065 3066 if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */ 3067 oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0)); 3068 oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0)); 3069 oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0)); 3070 oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0)); 3071 oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba )); 3072 } else if (instance->chan == 8) { /* start 8chan, kill 6,12chan commands */ 3073 oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0)); 3074 oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0)); 3075 oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0)); 3076 oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0)); 3077 oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea )); 3078 } else if (instance->chan == 12){ /* start 12chan, kill 6,12chan commands */ 3079 oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0)); 3080 oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0)); 3081 oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0)); 3082 oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0)); 3083 oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha )); 3084 } 3085 3086 instance->count = 1; 3087 instance->o_state = ONCORE_ALMANAC; 3088 oncore_log(instance, LOG_NOTICE, "state = ONCORE_ALMANAC"); 3089} 3090 3091 3092 3093/* 12chan position */ 3094 3095static void 3096oncore_msg_Ga( 3097 struct instance *instance, 3098 u_char *buf, 3099 size_t len 3100 ) 3101{ 3102 char Msg[160]; 3103 long lat, lon, ht; 3104 double Lat, Lon, Ht; 3105 3106 3107 lat = buf_w32(&buf[4]); 3108 lon = buf_w32(&buf[8]); 3109 ht = buf_w32(&buf[12]); /* GPS ellipsoid */ 3110 3111 Lat = lat; 3112 Lon = lon; 3113 Ht = ht; 3114 3115 Lat /= 3600000; 3116 Lon /= 3600000; 3117 Ht /= 100; 3118 3119 3120 snprintf(Msg, sizeof(Msg), 3121 "Ga Posn Lat = %.7f, Lon = %.7f, Ht = %.2f", Lat, 3122 Lon, Ht); 3123 oncore_log(instance, LOG_NOTICE, Msg); 3124 3125 instance->ss_lat = lat; 3126 instance->ss_long = lon; 3127 instance->ss_ht = ht; 3128 3129 oncore_print_posn(instance); 3130} 3131 3132 3133 3134/* 12 chan time/date */ 3135 3136static void 3137oncore_msg_Gb( 3138 struct instance *instance, 3139 u_char *buf, 3140 size_t len 3141 ) 3142{ 3143 char Msg[160]; 3144 const char *gmts; 3145 int mo, d, y, h, m, s, gmth, gmtm; 3146 3147 mo = buf[4]; 3148 d = buf[5]; 3149 y = 256*buf[6]+buf[7]; 3150 3151 h = buf[8]; 3152 m = buf[9]; 3153 s = buf[10]; 3154 3155 gmts = ((buf[11] == 0) ? "+" : "-"); 3156 gmth = buf[12]; 3157 gmtm = buf[13]; 3158 3159 snprintf(Msg, sizeof(Msg), 3160 "Date/Time set to: %d%s%d %2d:%02d:%02d GMT (GMT offset is %s%02d:%02d)", 3161 d, Month[mo-1], y, h, m, s, gmts, gmth, gmtm); 3162 oncore_log(instance, LOG_NOTICE, Msg); 3163} 3164 3165 3166 3167/* Leap Second for M12, gives all info from satellite message */ 3168/* also in UT v3.0 */ 3169 3170static void 3171oncore_msg_Gj( 3172 struct instance *instance, 3173 u_char *buf, 3174 size_t len 3175 ) 3176{ 3177 int dt; 3178 char Msg[160]; 3179 const char *cp; 3180 3181 instance->saw_Gj = 1; /* flag, saw_Gj, dont need to try Bj in check_leap */ 3182 3183 /* print the message to verify whats there */ 3184 3185 dt = buf[5] - buf[4]; 3186 3187 snprintf(Msg, sizeof(Msg), 3188 "Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d", buf[4], 3189 buf[5], 256 * buf[6] + buf[7], buf[8], buf[9], buf[10], 3190 (buf[14] + 256 * 3191 (buf[13] + 256 * (buf[12] + 256 * buf[11]))), 3192 buf[15], buf[16], buf[17]); 3193 oncore_log(instance, LOG_INFO, Msg); 3194 3195 if (dt) { 3196 snprintf(Msg, sizeof(Msg), 3197 "Leap second (%d) scheduled for %d%s%d at %d:%d:%d", 3198 dt, buf[9], Month[buf[8] - 1], 3199 256 * buf[6] + buf[7], buf[15], buf[16], 3200 buf[17]); 3201 oncore_log(instance, LOG_NOTICE, Msg); 3202 } 3203 3204 /* Only raise warning within a month of the leap second */ 3205 3206 instance->pp->leap = LEAP_NOWARNING; 3207 cp = "Set pp.leap to LEAP_NOWARNING"; 3208 3209 if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */ 3210 buf[8] == instance->BEHa[4]) { /* month */ 3211 if (dt) { 3212 if (dt < 0) { 3213 instance->pp->leap = LEAP_DELSECOND; 3214 cp = "Set pp.leap to LEAP_DELSECOND"; 3215 } else { 3216 instance->pp->leap = LEAP_ADDSECOND; 3217 cp = "Set pp.leap to LEAP_ADDSECOND"; 3218 } 3219 } 3220 } 3221 oncore_log(instance, LOG_INFO, cp); 3222} 3223 3224 3225 3226/* Power on failure */ 3227 3228static void 3229oncore_msg_Sz( 3230 struct instance *instance, 3231 u_char *buf, 3232 size_t len 3233 ) 3234{ 3235 if (instance && instance->peer) { 3236 oncore_log(instance, LOG_ERR, "Oncore: System Failure at Power On"); 3237 oncore_shutdown(instance->unit, instance->peer); 3238 } 3239} 3240 3241/************** Small Subroutines ***************/ 3242 3243 3244static void 3245oncore_antenna_report( 3246 struct instance *instance, 3247 enum antenna_state new_state) 3248{ 3249 const char *cp; 3250 3251 if (instance->ant_state == new_state) 3252 return; 3253 3254 switch (new_state) { 3255 case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK"; break; 3256 case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)"; break; 3257 case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break; 3258 case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)"; break; 3259 default: cp = "GPS antenna: ?"; break; 3260 } 3261 3262 instance->ant_state = new_state; 3263 oncore_log(instance, LOG_NOTICE, cp); 3264} 3265 3266 3267 3268static void 3269oncore_chan_test( 3270 struct instance *instance 3271 ) 3272{ 3273 /* subroutine oncore_Cj_id has determined the number of channels from the 3274 * model number of the attached oncore. This is not always correct since 3275 * the oncore could have non-standard firmware. Here we check (independently) by 3276 * trying a 6, 8, and 12 chan command, and see which responds. 3277 * Caution: more than one CAN respond. 3278 * 3279 * This #chan is used by the code rather than that calculated from the model number. 3280 */ 3281 3282 instance->o_state = ONCORE_CHECK_CHAN; 3283 oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_CHAN"); 3284 3285 instance->count3 = 1; 3286 oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba)); 3287 oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea)); 3288 oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha)); 3289} 3290 3291 3292 3293/* check for a GOOD Almanac, have we got one yet? */ 3294 3295static void 3296oncore_check_almanac( 3297 struct instance *instance 3298 ) 3299{ 3300 if (instance->chan == 6) { 3301 instance->rsm.bad_almanac = instance->BEHa[64]&0x1; 3302 instance->rsm.bad_fix = instance->BEHa[64]&0x52; 3303 } else if (instance->chan == 8) { 3304 instance->rsm.bad_almanac = instance->BEHa[72]&0x1; 3305 instance->rsm.bad_fix = instance->BEHa[72]&0x52; 3306 } else if (instance->chan == 12) { 3307 int bits1, bits2, bits3; 3308 3309 bits1 = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */ 3310 bits2 = instance->BEHa[130]; 3311 instance->rsm.bad_almanac = (bits2 & 0x80); 3312 instance->rsm.bad_fix = (bits2 & 0x8) || (bits1 == 0x2); 3313 /* too few sat Bad Geom */ 3314 3315 bits3 = instance->BEHa[141]; /* UTC parameters */ 3316 if (!instance->count5_set && (bits3 & 0xC0)) { 3317 instance->count5 = 2; 3318 instance->count5_set = 1; 3319 } 3320#ifdef ONCORE_VERBOSE_CHECK_ALMANAC 3321 { 3322 char Msg[160]; 3323 3324 snprintf(Msg, sizeof(Msg), 3325 "DEBUG BITS: (%x %x), (%x %x %x), %x %x %x %x %x", 3326 instance->BEHa[129], 3327 instance->BEHa[130], bits1, bits2, 3328 bits3, instance->mode == MODE_0D, 3329 instance->mode == MODE_2D, 3330 instance->mode == MODE_3D, 3331 instance->rsm.bad_almanac, 3332 instance->rsm.bad_fix); 3333 oncore_log(instance, LOG_DEBUG, Msg); 3334 } 3335#endif 3336 } 3337} 3338 3339 3340 3341/* check the antenna for changes (did it get unplugged?) */ 3342 3343static void 3344oncore_check_antenna( 3345 struct instance *instance 3346 ) 3347{ 3348 enum antenna_state antenna; /* antenna state */ 3349 3350 antenna = instance->ant_state; 3351 if (instance->chan == 12) 3352 antenna = (instance->BEHa[130] & 0x6 ) >> 1; 3353 else 3354 antenna = (instance->BEHa[37] & 0xc0) >> 6; /* prob unset 6, set GT, UT unset VP */ 3355 3356 oncore_antenna_report (instance, antenna); 3357} 3358 3359 3360 3361/* 3362 * Check the leap second status once per day. 3363 * 3364 * Note that the ONCORE firmware for the Bj command is wrong at 3365 * least in the VP. 3366 * It starts advertising a LEAP SECOND as soon as the GPS satellite 3367 * data message (page 18, subframe 4) is updated to a date in the 3368 * future, and does not wait for the month that it will occur. 3369 * The event will usually be advertised several months in advance. 3370 * Since there is a one bit flag, there is no way to tell if it is 3371 * this month, or when... 3372 * 3373 * As such, we have the workaround below, of only checking for leap 3374 * seconds with the Bj command in June/December. 3375 * 3376 * The Gj command gives more information, and we can tell in which 3377 * month to apply the correction. 3378 * 3379 * Note that with the VP we COULD read the raw data message, and 3380 * interpret it ourselves, but since this is specific to this receiver 3381 * only, and the above workaround is adequate, we don't bother. 3382 */ 3383 3384static void 3385oncore_check_leap_sec( 3386 struct instance *instance 3387 ) 3388{ 3389 oncore_cmd_Bl[2] = 1; /* just to be sure */ 3390 if (instance->Bj_day != instance->BEHa[5]) { /* do this 1/day */ 3391 instance->Bj_day = instance->BEHa[5]; 3392 3393 if (instance->saw_Gj < 0) { /* -1 DONT have Gj use Bj */ 3394 if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) 3395 oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj)); 3396 oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl)); 3397 return; 3398 } 3399 3400 if (instance->saw_Gj == 0) /* 0 is dont know if we have Gj */ 3401 instance->count4 = 1; 3402 3403 oncore_sendmsg(instance, oncore_cmd_Gj, sizeof(oncore_cmd_Gj)); 3404 return; 3405 } 3406 3407 /* Gj works for some 6/8 chan UT and the M12 */ 3408 /* if no response from Gj in 5 sec, we try Bj */ 3409 /* which isnt implemented in all the GT/UT either */ 3410 3411 if (instance->count4) { /* delay, waiting for Gj response */ 3412 if (instance->saw_Gj == 1) 3413 instance->count4 = 0; 3414 else if (instance->count4++ > 5) { /* delay, waiting for Gj response */ 3415 instance->saw_Gj = -1; /* didnt see it, will use Bj */ 3416 instance->count4 = 0; 3417 if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) 3418 oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj)); 3419 oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl)); 3420 } 3421 } 3422} 3423 3424 3425 3426/* check the message checksum, 3427 * buf points to START of message ( @@ ) 3428 * len is length WITH CR/LF. 3429 */ 3430 3431static int 3432oncore_checksum_ok( 3433 u_char *buf, 3434 int len 3435 ) 3436{ 3437 int i, j; 3438 3439 j = 0; 3440 for (i = 2; i < len-3; i++) 3441 j ^= buf[i]; 3442 3443 return(j == buf[len-3]); 3444} 3445 3446 3447 3448static void 3449oncore_compute_dH( 3450 struct instance *instance 3451 ) 3452{ 3453 int GPS, MSL; 3454 char Msg[160]; 3455 3456 /* Here calculate dH = GPS - MSL for output message */ 3457 /* also set Altitude Hold mode if GT */ 3458 3459 instance->have_dH = 1; 3460 if (instance->chan == 12) { 3461 GPS = buf_w32(&instance->BEHa[39]); 3462 MSL = buf_w32(&instance->BEHa[43]); 3463 } else { 3464 GPS = buf_w32(&instance->BEHa[23]); 3465 MSL = buf_w32(&instance->BEHa[27]); 3466 } 3467 instance->dH = GPS - MSL; 3468 instance->dH /= 100.; 3469 3470 /* if MSL is not set, the calculation is meaningless */ 3471 3472 if (MSL) { /* not set ! */ 3473 snprintf(Msg, sizeof(Msg), "dH = (GPS - MSL) = %.2fm", 3474 instance->dH); 3475 oncore_log(instance, LOG_INFO, Msg); 3476 } 3477} 3478 3479 3480 3481/* 3482 * try loading Almanac from shmem (where it was copied from shmem_old 3483 */ 3484 3485static void 3486oncore_load_almanac( 3487 struct instance *instance 3488 ) 3489{ 3490 u_char *cp, Cmd[20]; 3491 int n; 3492 struct timeval tv; 3493 struct tm *tm; 3494 3495 if (!instance->shmem) 3496 return; 3497 3498#ifndef ONCORE_VERBOSE_LOAD_ALMANAC 3499 for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2)); 3500 cp += (n + 3)) { 3501 if (!strncmp((char *) cp, "@@Cb", 4) && 3502 oncore_checksum_ok(cp, 33) && 3503 (*(cp+4) == 4 || *(cp+4) == 5)) { 3504 write(instance->ttyfd, cp, n); 3505 oncore_print_Cb(instance, cp); 3506 } 3507 } 3508#else /* ONCORE_VERBOSE_LOAD_ALMANAC follows */ 3509 for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2)); 3510 cp += (n+3)) { 3511 char Msg[160]; 3512 3513 snprintf(Msg, sizeof(Msg), "See %c%c%c%c %d", *(cp), 3514 *(cp+1), *(cp+2), *(cp+3), *(cp+4)); 3515 oncore_log(instance, LOG_DEBUG, Msg); 3516 3517 if (!strncmp(cp, "@@Cb", 4)) { 3518 oncore_print_Cb(instance, cp); 3519 if (oncore_checksum_ok(cp, 33)) { 3520 if (*(cp+4) == 4 || *(cp+4) == 5) { 3521 oncore_log(instance, LOG_DEBUG, "GOOD SF"); 3522 write(instance->ttyfd, cp, n); 3523 } else 3524 oncore_log(instance, LOG_DEBUG, "BAD SF"); 3525 } else 3526 oncore_log(instance, LOG_DEBUG, "BAD CHECKSUM"); 3527 } 3528 } 3529#endif 3530 3531 /* Must load position and time or the Almanac doesn't do us any good */ 3532 3533 if (!instance->posn_set) { /* if we input a posn use it, else from SHMEM */ 3534 oncore_log(instance, LOG_NOTICE, "Loading Posn from SHMEM"); 3535 for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) { 3536 if ((instance->chan == 6 && (!strncmp((char *) cp, "@@Ba", 4) && oncore_checksum_ok(cp, 68))) || 3537 (instance->chan == 8 && (!strncmp((char *) cp, "@@Ea", 4) && oncore_checksum_ok(cp, 76))) || 3538 (instance->chan == 12 && (!strncmp((char *) cp, "@@Ha", 4) && oncore_checksum_ok(cp, 154)))) { 3539 int ii, jj, kk; 3540 3541 instance->posn_set = 1; 3542 ii = buf_w32(cp + 15); 3543 jj = buf_w32(cp + 19); 3544 kk = buf_w32(cp + 23); 3545#ifdef ONCORE_VERBOSE_LOAD_ALMANAC 3546 { 3547 char Msg[160]; 3548 snprintf(Msg, sizeof(Msg), 3549 "SHMEM posn = %ld (%d, %d, %d)", 3550 (long)(cp-instance->shmem), 3551 ii, jj, kk); 3552 oncore_log(instance, LOG_DEBUG, Msg); 3553 } 3554#endif 3555 if (ii != 0 || jj != 0 || kk != 0) { /* phk asked for this test */ 3556 instance->ss_lat = ii; 3557 instance->ss_long = jj; 3558 instance->ss_ht = kk; 3559 } 3560 } 3561 } 3562 } 3563 oncore_set_posn(instance); 3564 3565 /* and set time to time from Computer clock */ 3566 3567 GETTIMEOFDAY(&tv, 0); 3568 tm = gmtime((const time_t *) &tv.tv_sec); 3569 3570#ifdef ONCORE_VERBOSE_LOAD_ALMANAC 3571 { 3572 char Msg[160]; 3573 snprintf(Msg, sizeof(Msg), "DATE %d %d %d, %d %d %d", 3574 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, 3575 tm->tm_hour, tm->tm_min, tm->tm_sec); 3576 oncore_log(instance, LOG_DEBUG, Msg); 3577 } 3578#endif 3579 if (instance->chan == 12) { 3580 memcpy(Cmd, oncore_cmd_Gb, (size_t) sizeof(oncore_cmd_Gb)); 3581 Cmd[-2+4] = tm->tm_mon + 1; 3582 Cmd[-2+5] = tm->tm_mday; 3583 Cmd[-2+6] = (1900+tm->tm_year)/256; 3584 Cmd[-2+7] = (1900+tm->tm_year)%256; 3585 Cmd[-2+8] = tm->tm_hour; 3586 Cmd[-2+9] = tm->tm_min; 3587 Cmd[-2+10] = tm->tm_sec; 3588 Cmd[-2+11] = 0; 3589 Cmd[-2+12] = 0; 3590 Cmd[-2+13] = 0; 3591 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Gb)); 3592 } else { 3593 /* First set GMT offset to zero */ 3594 3595 oncore_sendmsg(instance, oncore_cmd_Ab, sizeof(oncore_cmd_Ab)); 3596 3597 memcpy(Cmd, oncore_cmd_Ac, (size_t) sizeof(oncore_cmd_Ac)); 3598 Cmd[-2+4] = tm->tm_mon + 1; 3599 Cmd[-2+5] = tm->tm_mday; 3600 Cmd[-2+6] = (1900+tm->tm_year)/256; 3601 Cmd[-2+7] = (1900+tm->tm_year)%256; 3602 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ac)); 3603 3604 memcpy(Cmd, oncore_cmd_Aa, (size_t) sizeof(oncore_cmd_Aa)); 3605 Cmd[-2+4] = tm->tm_hour; 3606 Cmd[-2+5] = tm->tm_min; 3607 Cmd[-2+6] = tm->tm_sec; 3608 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Aa)); 3609 } 3610 3611 oncore_log(instance, LOG_INFO, "Setting Posn and Time after Loading Almanac"); 3612} 3613 3614 3615 3616/* Almanac data input */ 3617 3618static void 3619oncore_print_Cb( 3620 struct instance *instance, 3621 u_char *cp 3622 ) 3623{ 3624#ifdef ONCORE_VERBOSE_CB 3625 int ii; 3626 char Msg[160], Msg2[10]; 3627 3628 snprintf(Msg, sizeof(Msg), "DEBUG: See: %c%c%c%c", *(cp), 3629 *(cp+1), *(cp+2), *(cp+3)); 3630 oncore_log(instance, LOG_DEBUG, Msg); 3631 snprintf(Msg, sizeof(Msg), "DEBUG: Cb: [%d,%d]", *(cp+4), 3632 *(cp+5)); 3633 for(ii = 0; ii < 33; ii++) { 3634 snprintf(Msg2, sizeof(Msg2), " %d", *(cp+ii)); 3635 strncat(Msg, Msg2, sizeof(Msg)); 3636 } 3637 oncore_log(instance, LOG_DEBUG, Msg); 3638 3639 snprintf(Msg, sizeof(Msg), "Debug: Cb: [%d,%d]", *(cp+4), 3640 *(cp+5)); 3641 oncore_log(instance, LOG_DEBUG, Msg); 3642#endif 3643} 3644 3645 3646#if 0 3647static void 3648oncore_print_array( 3649 u_char *cp, 3650 int n 3651 ) 3652{ 3653 int jj, i, j, nn; 3654 3655 nn = 0; 3656 printf("\nTOP\n"); 3657 jj = n/16; 3658 for (j=0; j<jj; j++) { 3659 printf("%4d: ", nn); 3660 nn += 16; 3661 for (i=0; i<16; i++) 3662 printf(" %o", *cp++); 3663 printf("\n"); 3664 } 3665} 3666#endif 3667 3668 3669static void 3670oncore_print_posn( 3671 struct instance *instance 3672 ) 3673{ 3674 char Msg[120], ew, ns; 3675 double xd, xm, xs, yd, ym, ys, hm, hft; 3676 int idx, idy, is, imx, imy; 3677 long lat, lon; 3678 3679 oncore_log(instance, LOG_INFO, "Posn:"); 3680 ew = 'E'; 3681 lon = instance->ss_long; 3682 if (lon < 0) { 3683 ew = 'W'; 3684 lon = -lon; 3685 } 3686 3687 ns = 'N'; 3688 lat = instance->ss_lat; 3689 if (lat < 0) { 3690 ns = 'S'; 3691 lat = -lat; 3692 } 3693 3694 hm = instance->ss_ht/100.; 3695 hft= hm/0.3048; 3696 3697 xd = lat/3600000.; /* lat, lon in int msec arc, ht in cm. */ 3698 yd = lon/3600000.; 3699 snprintf(Msg, sizeof(Msg), 3700 "Lat = %c %11.7fdeg, Long = %c %11.7fdeg, Alt = %5.2fm (%5.2fft) GPS", 3701 ns, xd, ew, yd, hm, hft); 3702 oncore_log(instance, LOG_INFO, Msg); 3703 3704 idx = xd; 3705 idy = yd; 3706 imx = lat%3600000; 3707 imy = lon%3600000; 3708 xm = imx/60000.; 3709 ym = imy/60000.; 3710 snprintf(Msg, sizeof(Msg), 3711 "Lat = %c %3ddeg %7.4fm, Long = %c %3ddeg %8.5fm, Alt = %7.2fm (%7.2fft) GPS", ns, idx, xm, ew, idy, ym, hm, hft); 3712 oncore_log(instance, LOG_INFO, Msg); 3713 3714 imx = xm; 3715 imy = ym; 3716 is = lat%60000; 3717 xs = is/1000.; 3718 is = lon%60000; 3719 ys = is/1000.; 3720 snprintf(Msg, sizeof(Msg), 3721 "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS", 3722 ns, idx, imx, xs, ew, idy, imy, ys, hm, hft); 3723 oncore_log(instance, LOG_INFO, Msg); 3724} 3725 3726 3727 3728/* 3729 * write message to Oncore. 3730 */ 3731 3732static void 3733oncore_sendmsg( 3734 struct instance *instance, 3735 u_char *ptr, 3736 size_t len 3737 ) 3738{ 3739 int fd; 3740 u_char cs = 0; 3741 3742 fd = instance->ttyfd; 3743#ifdef ONCORE_VERBOSE_SENDMSG 3744 if (debug > 4) { 3745 char Msg[120]; 3746 3747 snprintf(Msg, sizeof(Msg), "ONCORE: Send @@%c%c %d", 3748 ptr[0], ptr[1], (int)len); 3749 oncore_log(instance, LOG_DEBUG, Msg); 3750 } 3751#endif 3752 write(fd, "@@", (size_t) 2); 3753 write(fd, ptr, len); 3754 while (len--) 3755 cs ^= *ptr++; 3756 write(fd, &cs, (size_t) 1); 3757 write(fd, "\r\n", (size_t) 2); 3758} 3759 3760 3761 3762static void 3763oncore_set_posn( 3764 struct instance *instance 3765 ) 3766{ 3767 int mode; 3768 u_char Cmd[20]; 3769 3770 /* Turn OFF position hold, it needs to be off to set position (for some units), 3771 will get set ON in @@Ea later */ 3772 3773 if (instance->chan == 12) 3774 oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */ 3775 else { 3776 oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */ 3777 oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* (6/8) */ 3778 } 3779 3780 mode = instance->init_type; 3781 3782 if (mode != 0) { /* first set posn hold position */ 3783 memcpy(Cmd, oncore_cmd_As, (size_t) sizeof(oncore_cmd_As)); /* don't modify static variables */ 3784 w32_buf(&Cmd[-2+4], (int) instance->ss_lat); 3785 w32_buf(&Cmd[-2+8], (int) instance->ss_long); 3786 w32_buf(&Cmd[-2+12], (int) instance->ss_ht); 3787 Cmd[-2+16] = 0; 3788 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_As)); /* posn hold 3D posn (6/8/12) */ 3789 3790 memcpy(Cmd, oncore_cmd_Au, (size_t) sizeof(oncore_cmd_Au)); 3791 w32_buf(&Cmd[-2+4], (int) instance->ss_ht); 3792 Cmd[-2+8] = 0; 3793 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Au)); /* altitude hold (6/8/12 not UT, M12T) */ 3794 3795 /* next set current position */ 3796 3797 if (instance->chan == 12) { 3798 memcpy(Cmd, oncore_cmd_Ga, (size_t) sizeof(oncore_cmd_Ga)); 3799 w32_buf(&Cmd[-2+4], (int) instance->ss_lat); 3800 w32_buf(&Cmd[-2+8], (int) instance->ss_long); 3801 w32_buf(&Cmd[-2+12],(int) instance->ss_ht); 3802 Cmd[-2+16] = 0; 3803 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ga)); /* 3d posn (12) */ 3804 } else { 3805 memcpy(Cmd, oncore_cmd_Ad, (size_t) sizeof(oncore_cmd_Ad)); 3806 w32_buf(&Cmd[-2+4], (int) instance->ss_lat); 3807 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ad)); /* lat (6/8) */ 3808 3809 memcpy(Cmd, oncore_cmd_Ae, (size_t) sizeof(oncore_cmd_Ae)); 3810 w32_buf(&Cmd[-2+4], (int) instance->ss_long); 3811 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ae)); /* long (6/8) */ 3812 3813 memcpy(Cmd, oncore_cmd_Af, (size_t) sizeof(oncore_cmd_Af)); 3814 w32_buf(&Cmd[-2+4], (int) instance->ss_ht); 3815 Cmd[-2+8] = 0; 3816 oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Af)); /* ht (6/8) */ 3817 } 3818 3819 /* Finally, turn on position hold */ 3820 3821 if (instance->chan == 12) 3822 oncore_sendmsg(instance, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); 3823 else 3824 oncore_sendmsg(instance, oncore_cmd_At1, sizeof(oncore_cmd_At1)); 3825 } 3826} 3827 3828 3829 3830static void 3831oncore_set_traim( 3832 struct instance *instance 3833 ) 3834{ 3835 char Msg[160]; 3836 3837 if (instance->traim_in != -1) /* set in Input */ 3838 instance->traim = instance->traim_in; 3839 else 3840 instance->traim = instance->traim_ck; 3841 3842 snprintf(Msg, sizeof(Msg), "Input says TRAIM = %d", 3843 instance->traim_in); 3844 oncore_log(instance, LOG_INFO, Msg); 3845 snprintf(Msg, sizeof(Msg), "Model # says TRAIM = %d", 3846 instance->traim_id); 3847 oncore_log(instance, LOG_INFO, Msg); 3848 snprintf(Msg, sizeof(Msg), "Testing says TRAIM = %d", 3849 instance->traim_ck); 3850 oncore_log(instance, LOG_INFO, Msg); 3851 snprintf(Msg, sizeof(Msg), "Using TRAIM = %d", 3852 instance->traim); 3853 oncore_log(instance, LOG_INFO, Msg); 3854 3855 if (instance->traim_ck == 1 && instance->traim == 0) { 3856 /* if it should be off, and I turned it on during testing, 3857 then turn it off again */ 3858 if (instance->chan == 6) 3859 oncore_sendmsg(instance, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx)); 3860 else if (instance->chan == 8) 3861 oncore_sendmsg(instance, oncore_cmd_Enx, sizeof(oncore_cmd_Enx)); 3862 else /* chan == 12 */ 3863 oncore_sendmsg(instance, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0)); 3864 oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0)); 3865 } 3866} 3867 3868 3869 3870/* 3871 * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn. 3872 */ 3873 3874static void 3875oncore_shmem_get_3D( 3876 struct instance *instance 3877 ) 3878{ 3879 if (instance->pp->second%15 == 3) { /* start the sequence */ /* by changing mode */ 3880 instance->shmem_reset = 1; 3881 if (instance->chan == 12) { 3882 if (instance->shmem_Posn == 2) 3883 oncore_sendmsg(instance, oncore_cmd_Gd2, sizeof(oncore_cmd_Gd2)); /* 2D */ 3884 else 3885 oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* 3D */ 3886 } else { 3887 if (instance->saw_At) { /* out of 0D -> 3D mode */ 3888 oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); 3889 if (instance->shmem_Posn == 2) /* 3D -> 2D mode */ 3890 oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); 3891 } else 3892 oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); 3893 } 3894 } else if (instance->shmem_reset || (instance->mode != MODE_0D)) { 3895 instance->shmem_reset = 0; 3896 if (instance->chan == 12) 3897 oncore_sendmsg(instance, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); /* 0D */ 3898 else { 3899 if (instance->saw_At) { 3900 if (instance->mode == MODE_2D) /* 2D -> 3D or 0D mode */ 3901 oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); 3902 oncore_sendmsg(instance, oncore_cmd_At1, sizeof(oncore_cmd_At1)); /* to 0D mode */ 3903 } else 3904 oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); 3905 } 3906 } 3907} 3908 3909 3910 3911/* 3912 * Here we do the Software SiteSurvey. 3913 * We have to average our own position for the Position Hold Mode 3914 * We use Heights from the GPS ellipsoid. 3915 * We check for the END of either HW or SW SiteSurvey. 3916 */ 3917 3918static void 3919oncore_ss( 3920 struct instance *instance 3921 ) 3922{ 3923 char Msg[160]; 3924 double lat, lon, ht; 3925 3926 3927 if (instance->site_survey == ONCORE_SS_HW) { 3928 /* 3929 * Check to see if Hardware SiteSurvey has Finished. 3930 */ 3931 3932 if ((instance->chan == 8 && !(instance->BEHa[37] & 0x20)) || 3933 (instance->chan == 12 && !(instance->BEHa[130] & 0x10))) { 3934 oncore_log(instance, LOG_INFO, "Now in 0D mode"); 3935 3936 if (instance->chan == 12) 3937 oncore_sendmsg(instance, oncore_cmd_Gax, sizeof(oncore_cmd_Gax)); 3938 else 3939 oncore_sendmsg(instance, oncore_cmd_Asx, sizeof(oncore_cmd_Asx)); 3940 3941 oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE"); 3942 instance->site_survey = ONCORE_SS_DONE; 3943 } 3944 } else { 3945 /* 3946 * Must be a Software Site Survey. 3947 */ 3948 3949 if (instance->rsm.bad_fix) /* Not if poor geometry or less than 3 sats */ 3950 return; 3951 3952 if (instance->mode != MODE_3D) /* Use only 3D Fixes */ 3953 return; 3954 3955 instance->ss_lat += buf_w32(&instance->BEHa[15]); 3956 instance->ss_long += buf_w32(&instance->BEHa[19]); 3957 instance->ss_ht += buf_w32(&instance->BEHa[23]); /* GPS ellipsoid */ 3958 instance->ss_count++; 3959 3960 if (instance->ss_count != POS_HOLD_AVERAGE) 3961 return; 3962 3963 instance->ss_lat /= POS_HOLD_AVERAGE; 3964 instance->ss_long /= POS_HOLD_AVERAGE; 3965 instance->ss_ht /= POS_HOLD_AVERAGE; 3966 3967 snprintf(Msg, sizeof(Msg), 3968 "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)", 3969 instance->ss_lat, instance->ss_long, 3970 instance->ss_ht); 3971 oncore_log(instance, LOG_NOTICE, Msg); 3972 lat = instance->ss_lat/3600000.; 3973 lon = instance->ss_long/3600000.; 3974 ht = instance->ss_ht/100; 3975 snprintf(Msg, sizeof(Msg), 3976 "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)", 3977 lat, lon, ht); 3978 oncore_log(instance, LOG_NOTICE, Msg); 3979 3980 oncore_set_posn(instance); 3981 3982 oncore_log(instance, LOG_INFO, "Now in 0D mode"); 3983 3984 oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE"); 3985 instance->site_survey = ONCORE_SS_DONE; 3986 } 3987} 3988 3989 3990 3991static int 3992oncore_wait_almanac( 3993 struct instance *instance 3994 ) 3995{ 3996 if (instance->rsm.bad_almanac) { 3997 instance->counta++; 3998 if (instance->counta%5 == 0) 3999 oncore_log(instance, LOG_INFO, "Waiting for Almanac"); 4000 4001 /* 4002 * If we get here (first time) then we don't have an almanac in memory. 4003 * Check if we have a SHMEM, and if so try to load whatever is there. 4004 */ 4005 4006 if (!instance->almanac_from_shmem) { 4007 instance->almanac_from_shmem = 1; 4008 oncore_load_almanac(instance); 4009 } 4010 return(1); 4011 } else { /* Here we have the Almanac, we will be starting the @@Bn/@@En/@@Hn 4012 commands, and can finally check for TRAIM. Again, we set a delay 4013 (5sec) and wait for things to settle down */ 4014 4015 if (instance->chan == 6) 4016 oncore_sendmsg(instance, oncore_cmd_Bn, sizeof(oncore_cmd_Bn)); 4017 else if (instance->chan == 8) 4018 oncore_sendmsg(instance, oncore_cmd_En, sizeof(oncore_cmd_En)); 4019 else if (instance->chan == 12) { 4020 oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* 1PPS on, continuous */ 4021 oncore_sendmsg(instance, oncore_cmd_Ge, sizeof(oncore_cmd_Ge)); /* TRAIM on */ 4022 oncore_sendmsg(instance, oncore_cmd_Hn, sizeof(oncore_cmd_Hn)); /* TRAIM status 1/s */ 4023 } 4024 instance->traim_delay = 1; 4025 4026 oncore_log(instance, LOG_NOTICE, "Have now loaded an ALMANAC"); 4027 4028 instance->o_state = ONCORE_RUN; 4029 oncore_log(instance, LOG_NOTICE, "state = ONCORE_RUN"); 4030 } 4031 return(0); 4032} 4033 4034 4035 4036static void 4037oncore_log ( 4038 struct instance *instance, 4039 int log_level, 4040 const char *msg 4041 ) 4042{ 4043 int i; 4044 char Msg[200]; 4045 4046 snprintf(Msg, sizeof(Msg), "ONCORE[%d]: %s", instance->unit, 4047 msg); 4048 syslog(log_level, Msg); 4049 4050 i = strlen(msg); 4051 4052 if (i > 127) { 4053 snprintf(Msg, sizeof(Msg), 4054 "Internal Error: max error msg length exceeded in clockstats file (%d)", 4055 i); 4056 record_clock_stats(&(instance->peer->srcadr), Msg); 4057 record_clock_stats(&(instance->peer->srcadr), "Start of message was"); 4058 strncpy(Msg, msg, 120); 4059 record_clock_stats(&(instance->peer->srcadr), Msg); 4060 } else { /* now put ONCORE[n]: ahead of message if it will fit */ 4061 if (i < 110) { 4062 snprintf(Msg, sizeof(Msg), "ONCORE[%d]: %s", 4063 instance->unit, msg); 4064 record_clock_stats(&(instance->peer->srcadr), Msg); 4065 } else 4066 record_clock_stats(&(instance->peer->srcadr), msg); 4067 } 4068 4069#ifdef ONCORE_VERBOSE_ONCORE_LOG 4070 instance->max_len = max(i, instance->max_len); 4071 instance->max_count++; 4072 if (instance->max_count % 100 == 0) { 4073 snprintf(Msg, sizeof(Msg), 4074 "Max Message Length so far is %d", 4075 instance->max_len); 4076 oncore_log(instance, LOG_INFO, Msg); 4077 } 4078#endif 4079} 4080 4081#else 4082int refclock_oncore_bs; 4083#endif /* REFCLOCK && CLOCK_ONCORE */ 4084