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