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